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