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