xref: /illumos-gate/usr/src/cmd/sgs/rtld/common/paths.c (revision fe0e7ec4)
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 2005 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 
826 	for (pnp = opnp = 0, str = nlist; *nlist; str = nlist) {
827 		char	*ostr;
828 		size_t	len, olen;
829 		uint_t	tkns = 0;
830 
831 		if (*nlist == ';')
832 			++nlist, ++str;
833 		if (*nlist == ':') {
834 			if ((str = strdup(MSG_ORIG(MSG_FMT_CWD))) == NULL)
835 				return ((Pnode *)0);
836 			len = MSG_FMT_CWD_SIZE;
837 
838 			if (*nlist)
839 				nlist++;
840 		} else {
841 			char	*elist;
842 
843 			len = 0;
844 			while (*nlist && (*nlist != ':') && (*nlist != ';')) {
845 				nlist++, len++;
846 			}
847 
848 			if (*nlist)
849 				nlist++;
850 
851 			/*
852 			 * Expand the captured string.  Besides expanding the
853 			 * present path/file entry, we may have a new list to
854 			 * deal with (ISALIST expands to multiple new entries).
855 			 */
856 			elist = nlist;
857 			ostr = str;
858 			olen = len;
859 			if ((tkns = expand(&str, &len, &elist, orig, omit,
860 			    clmp)) == 0)
861 				return ((Pnode *)0);
862 
863 			if (elist != nlist) {
864 				if (olist)
865 					free(olist);
866 				nlist = olist = elist;
867 			}
868 		}
869 
870 		/*
871 		 * If this a secure application, validation of the expanded
872 		 * pathname may be necessary.
873 		 */
874 		if (rtld_flags & RT_FL_SECURE) {
875 			if (is_path_secure((const char *)str, clmp, orig,
876 			    tkns) == 0)
877 				continue;
878 		}
879 
880 		/*
881 		 * Allocate a new Pnode for this string.
882 		 */
883 		if ((npnp = calloc(1, sizeof (Pnode))) == 0)
884 			return ((Pnode *)0);
885 		if (opnp == 0)
886 			pnp = npnp;
887 		else
888 			opnp->p_next = npnp;
889 
890 		if ((orig & PN_SER_MASK) && (tkns & PN_TKN_MASK)) {
891 			char	*oname;
892 
893 			/*
894 			 * If this is a pathname, and any token expansion
895 			 * occurred, maintain the original string for possible
896 			 * diagnostic use.
897 			 */
898 			if ((oname = malloc(olen + 1)) == 0)
899 				return ((Pnode *)0);
900 			(void) strncpy(oname, ostr, olen);
901 			oname[olen] = '\0';
902 			npnp->p_oname = oname;
903 		}
904 		npnp->p_name = str;
905 		npnp->p_len = len;
906 		npnp->p_orig = (orig & (LA_SER_MASK | PN_SER_MASK)) |
907 		    (tkns & PN_TKN_MASK);
908 
909 		opnp = npnp;
910 	}
911 
912 	if (olist)
913 		free(olist);
914 
915 	return (pnp);
916 }
917