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