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 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include	<sys/mman.h>
30 #include	<sys/types.h>
31 #include	<sys/stat.h>
32 #include	<fcntl.h>
33 #include	<limits.h>
34 #include	<stdio.h>
35 #include	<string.h>
36 #include	<rtc.h>
37 #include	<debug.h>
38 #include	<conv.h>
39 #include	"_rtld.h"
40 #include	"msg.h"
41 
42 static Config	_config = { 0 };
43 Config *	config = &_config;
44 
45 
46 /*
47  * Validate a configuration file.
48  */
49 static void
50 elf_config_validate(Addr addr, Rtc_head *head, Rt_map *lmp)
51 {
52 	Lm_list		*lml = LIST(lmp);
53 	const char	*str, *strtbl = config->c_strtbl;
54 	Rtc_obj		*obj;
55 	Rtc_dir		*dirtbl;
56 	Rtc_file	*filetbl;
57 	struct stat	status;
58 	int		err;
59 
60 	/*
61 	 * If this configuration file is for a specific application make sure
62 	 * we've been invoked by the application.  Note that we only check the
63 	 * basename component of the application as the original application
64 	 * and its cached equivalent are never going to have the same pathnames.
65 	 * Also, we use PATHNAME() and not NAME() - this catches things like vi
66 	 * that exec shells using execv(/usr/bin/ksh, sh ...).
67 	 */
68 	if (head->ch_app) {
69 		char	*_str, *_cname, *cname, *aname = PATHNAME(lmp);
70 
71 		obj = (Rtc_obj *)(head->ch_app + addr);
72 		cname = _cname = (char *)(strtbl + obj->co_name);
73 
74 		if ((_str = strrchr(aname, '/')) != NULL)
75 			aname = ++_str;
76 		if ((_str = strrchr(cname, '/')) != NULL)
77 			cname = ++_str;
78 
79 		if (strcmp(aname, cname)) {
80 			/*
81 			 * It's possible a user is trying to ldd(1) an alternate
82 			 * shared object and point to a configuration file that
83 			 * the shared object is part of.  In this case ignore
84 			 * any mismatch name warnings.
85 			 */
86 			if ((lml->lm_flags & LML_FLG_TRC_ENABLE) &&
87 			    ((FLAGS1(lmp) & FL1_RT_LDDSTUB) == 0)) {
88 				eprintf(lml, ERR_WARNING,
89 				    MSG_INTL(MSG_CONF_APP), config->c_name,
90 				    _cname);
91 				return;
92 			}
93 		}
94 
95 		/*
96 		 * If we have a valid alternative application reset its original
97 		 * name for possible $ORIGIN processing.
98 		 */
99 		if ((FLAGS1(lmp) & FL1_RT_LDDSTUB) == 0) {
100 			ORIGNAME(lmp) = _cname;
101 			DIRSZ(lmp) = cname - _cname - 1;
102 		}
103 	}
104 
105 	/*
106 	 * If alternative objects are specified traverse the directories
107 	 * specified in the configuration file, if any directory is newer than
108 	 * the time it was recorded in the cache then continue to inspect its
109 	 * files.  Any file determined newer than its configuration recording
110 	 * questions the the use of any alternative objects.  The intent here
111 	 * is to make sure no-one abuses a configuration as a means of static
112 	 * linking.
113 	 */
114 	for (dirtbl = (Rtc_dir *)(head->ch_dir + addr);
115 	    dirtbl->cd_obj; dirtbl++) {
116 		/*
117 		 * Skip directories that provide no files - this also catches
118 		 * RTC_OBJ_NOEXIST directories.
119 		 */
120 		filetbl = (Rtc_file *)(dirtbl->cd_file + addr);
121 		if (filetbl->cf_obj == 0)
122 			continue;
123 
124 		/*
125 		 * Skip directories that haven't provided real, dumped files.
126 		 */
127 		obj = (Rtc_obj *)(dirtbl->cd_obj + addr);
128 		if ((obj->co_flags & (RTC_OBJ_DUMP | RTC_OBJ_REALPTH)) !=
129 		    (RTC_OBJ_DUMP | RTC_OBJ_REALPTH))
130 			continue;
131 
132 		str = strtbl + obj->co_name;
133 
134 		if (stat(str, &status) != 0) {
135 			err = errno;
136 			eprintf(lml, ERR_WARNING, MSG_INTL(MSG_CONF_DSTAT),
137 			    config->c_name, str, strerror(err));
138 			continue;
139 		}
140 
141 		if (status.st_mtime == obj->co_info)
142 			continue;
143 
144 		/*
145 		 * The system directory is newer than the configuration files
146 		 * entry, start checking any dumped files.
147 		 */
148 		for (; filetbl->cf_obj; filetbl++) {
149 			obj = (Rtc_obj *)(filetbl->cf_obj + addr);
150 			str = strtbl + obj->co_name;
151 
152 			/*
153 			 * Skip any files that aren't real, dumped files.
154 			 */
155 			if ((obj->co_flags &
156 			    (RTC_OBJ_DUMP | RTC_OBJ_REALPTH)) !=
157 			    (RTC_OBJ_DUMP | RTC_OBJ_REALPTH))
158 				continue;
159 
160 			if (stat(str, &status) != 0) {
161 				err = errno;
162 				eprintf(lml, ERR_WARNING,
163 				    MSG_INTL(MSG_CONF_FSTAT), config->c_name,
164 				    str, strerror(err));
165 				continue;
166 			}
167 
168 			/*
169 			 * If the files size is different somethings been
170 			 * changed.
171 			 */
172 			if (status.st_size != obj->co_info) {
173 				eprintf(lml, ERR_WARNING,
174 				    MSG_INTL(MSG_CONF_FCMP), config->c_name,
175 				    str);
176 			}
177 		}
178 	}
179 }
180 
181 int
182 elf_config(Rt_map *lmp, int aout)
183 {
184 	Rtc_id		*id;
185 	Rtc_head	*head;
186 	int		fd, features = 0;
187 	struct stat	status;
188 	Addr		addr;
189 	Pnode		*pnp;
190 	const char	*str = config->c_name;
191 
192 	/*
193 	 * If an alternative configuration file has been specified use it
194 	 * (expanding any tokens), otherwise try opening up the default.
195 	 */
196 	if ((str == 0) && ((rtld_flags & RT_FL_CONFAPP) == 0))
197 #if	defined(_ELF64)
198 		str = MSG_ORIG(MSG_PTH_CONFIG_64);
199 #else
200 		str = MSG_ORIG(MSG_PTH_CONFIG);
201 #endif
202 	else if (rtld_flags & RT_FL_SECURE)
203 		return (0);
204 	else {
205 		size_t	size;
206 		char	*name;
207 
208 		/*
209 		 * If we're dealing with an alternative application, fabricate
210 		 * the need for a $ORIGIN/ld.config.app-name configuration file.
211 		 */
212 		if (rtld_flags & RT_FL_CONFAPP) {
213 			char	_name[PATH_MAX];
214 
215 			if ((str = strrchr(PATHNAME(lmp), '/')) != NULL)
216 				str++;
217 			else
218 				str = PATHNAME(lmp);
219 
220 			(void) snprintf(_name, PATH_MAX,
221 			    MSG_ORIG(MSG_ORG_CONFIG), str);
222 			str = _name;
223 		}
224 
225 		size = strlen(str);
226 		name = (char *)str;
227 
228 		if (expand(&name, &size, 0, 0,
229 		    (PN_TKN_ISALIST | PN_TKN_HWCAP), lmp) == 0)
230 			return (0);
231 		str = (const char *)name;
232 	}
233 	config->c_name = str;
234 
235 	/*
236 	 * If we can't open the configuration file return silently.
237 	 */
238 	if ((fd = open(str, O_RDONLY, 0)) == -1)
239 		return (DBG_CONF_PRCFAIL);
240 
241 	/*
242 	 * Determine the configuration file size and map the file.
243 	 */
244 	(void) fstat(fd, &status);
245 	if (status.st_size < sizeof (Rtc_head)) {
246 		(void) close(fd);
247 		return (DBG_CONF_CORRUPT);
248 	}
249 	if ((addr = (Addr)mmap(0, status.st_size, PROT_READ, MAP_SHARED,
250 	    fd, 0)) == (Addr)MAP_FAILED) {
251 		(void) close(fd);
252 		return (DBG_CONF_PRCFAIL);
253 	}
254 	(void) close(fd);
255 
256 	/*
257 	 * If we have an Rtc_id block at the beginning, then validate it
258 	 * and advance the address to the Rtc_head. If not, then trust
259 	 * that the file is compatible with us and move ahead (there is
260 	 * some error checking for Rtc_head below as well).
261 	 */
262 	id = (Rtc_id *) addr;
263 	if (RTC_ID_TEST(id)) {
264 		addr += sizeof (*id);
265 		status.st_size -= sizeof (*id);
266 		if (status.st_size < sizeof (Rtc_head))
267 			return (DBG_CONF_CORRUPT);
268 		if ((id->id_class != M_CLASS) || (id->id_data != M_DATA) ||
269 		    (id->id_machine != M_MACH))
270 			return (DBG_CONF_ABIMISMATCH);
271 	}
272 
273 	config->c_bgn = addr;
274 	config->c_end = addr + status.st_size;
275 
276 	head = (Rtc_head *)addr;
277 
278 	/*
279 	 * Make sure we can handle this version of the configuration file.
280 	 */
281 	if (head->ch_version > RTC_VER_CURRENT)
282 		return (DBG_CONF_VERSION);
283 
284 	/*
285 	 * When crle(1) creates a temporary configuration file the
286 	 * RTC_HDR_IGNORE flag is set.  Thus the mapping of the configuration
287 	 * file is taken into account but not its content.
288 	 */
289 	if (head->ch_cnflags & RTC_HDR_IGNORE)
290 		return (DBG_CONF_IGNORE);
291 
292 	/*
293 	 * Apply any new default library pathname.
294 	 */
295 	if (head->ch_edlibpath) {
296 		str = (const char *)(head->ch_edlibpath + addr);
297 #ifndef	SGS_PRE_UNIFIED_PROCESS
298 		if ((head->ch_cnflags & RTC_HDR_UPM) == 0) {
299 #if	defined(_ELF64)
300 			str = conv_config_upm(str, MSG_ORIG(MSG_PTH_USRLIB_64),
301 			    MSG_ORIG(MSG_PTH_LIB_64), MSG_PTH_LIB_64_SIZE);
302 #else
303 			str = conv_config_upm(str, MSG_ORIG(MSG_PTH_USRLIB),
304 			    MSG_ORIG(MSG_PTH_LIB), MSG_PTH_LIB_SIZE);
305 #endif
306 		}
307 #endif
308 		if ((pnp = expand_paths(lmp, str,
309 		    (LA_SER_DEFAULT | LA_SER_CONFIG), PN_TKN_HWCAP)) != 0)
310 			elf_fct.fct_dflt_dirs = pnp;
311 		features |= CONF_EDLIBPATH;
312 	}
313 	if (head->ch_eslibpath) {
314 		str = (const char *)(head->ch_eslibpath + addr);
315 #ifndef	SGS_PRE_UNIFIED_PROCESS
316 		if ((head->ch_cnflags & RTC_HDR_UPM) == 0) {
317 #if	defined(_ELF64)
318 			str = conv_config_upm(str,
319 			    MSG_ORIG(MSG_PTH_USRLIBSE_64),
320 			    MSG_ORIG(MSG_PTH_LIBSE_64), MSG_PTH_LIBSE_64_SIZE);
321 #else
322 			str = conv_config_upm(str, MSG_ORIG(MSG_PTH_USRLIBSE),
323 			    MSG_ORIG(MSG_PTH_LIBSE), MSG_PTH_LIBSE_SIZE);
324 #endif
325 		}
326 #endif
327 		if ((pnp = expand_paths(lmp, str,
328 		    (LA_SER_SECURE | LA_SER_CONFIG), PN_TKN_HWCAP)) != 0)
329 			elf_fct.fct_secure_dirs = pnp;
330 		features |= CONF_ESLIBPATH;
331 	}
332 #if	defined(__sparc) && !defined(_ELF64)
333 	if (head->ch_adlibpath) {
334 		str = (const char *)(head->ch_adlibpath + addr);
335 		if ((pnp = expand_paths(lmp, str,
336 		    (LA_SER_DEFAULT | LA_SER_CONFIG), PN_TKN_HWCAP)) != 0)
337 			aout_fct.fct_dflt_dirs = pnp;
338 		features |= CONF_ADLIBPATH;
339 	}
340 	if (head->ch_aslibpath) {
341 		str = (const char *)(head->ch_aslibpath + addr);
342 		if ((pnp = expand_paths(lmp, str,
343 		    (LA_SER_SECURE | LA_SER_CONFIG), PN_TKN_HWCAP)) != 0)
344 			aout_fct.fct_secure_dirs = pnp;
345 		features |= CONF_ASLIBPATH;
346 	}
347 #endif
348 	/*
349 	 * Apply any environment variables.  This attribute was added with
350 	 * RTC_VER_THREE.
351 	 */
352 	if ((head->ch_version >= RTC_VER_THREE) && head->ch_env &&
353 	    (!(rtld_flags & RT_FL_NOENVCFG))) {
354 		if (readenv_config((Rtc_env *)(head->ch_env + addr),
355 		    addr, aout) != 0)
356 			return (-1);
357 		features |= CONF_ENVS;
358 	}
359 
360 	/*
361 	 * Determine whether filter/filtee associations are available.
362 	 */
363 	if ((head->ch_version >= RTC_VER_FOUR) && head->ch_fltr &&
364 	    (!(rtld_flags2 & RT_FL2_NOFLTCFG))) {
365 		rtld_flags2 |= RT_FL2_FLTCFG;
366 		config->c_fltr = (Rtc_fltr *)(head->ch_fltr + addr);
367 		config->c_flte = (Rtc_flte *)(head->ch_flte + addr);
368 		features |= CONF_FLTR;
369 	}
370 
371 	/*
372 	 * Determine whether directory configuration is available.
373 	 */
374 	if ((!(rtld_flags & RT_FL_NODIRCFG)) && head->ch_hash) {
375 		config->c_hashtbl = (Word *)(head->ch_hash + addr);
376 		config->c_hashchain = &config->c_hashtbl[2 +
377 		    config->c_hashtbl[0]];
378 		config->c_objtbl = (Rtc_obj *)(head->ch_obj + addr);
379 		config->c_strtbl = (const char *)(head->ch_str + addr);
380 
381 		rtld_flags |= RT_FL_DIRCFG;
382 		features |= CONF_DIRCFG;
383 	}
384 
385 	/*
386 	 * Determine whether alternative objects are specified or an object
387 	 * reservation area is required.  If the reservation can't be completed
388 	 * (either because the configuration information is out-of-date, or the
389 	 * the reservation can't be allocated), then alternative objects are
390 	 * ignored.
391 	 */
392 	if ((!(rtld_flags & (RT_FL_NODIRCFG | RT_FL_NOOBJALT))) &&
393 	    (head->ch_cnflags & RTC_HDR_ALTER)) {
394 		rtld_flags |= RT_FL_OBJALT;
395 		features |= CONF_OBJALT;
396 
397 		elf_config_validate(addr, head, lmp);
398 
399 		if (head->ch_resbgn) {
400 
401 			if (((config->c_bgn <= head->ch_resbgn) &&
402 			    (config->c_bgn >= head->ch_resend)) ||
403 			    (nu_map(LIST(lmp),
404 			    (caddr_t)(uintptr_t)head->ch_resbgn,
405 			    (head->ch_resend - head->ch_resbgn), PROT_NONE,
406 			    MAP_FIXED | MAP_PRIVATE) == MAP_FAILED))
407 				return (-1);
408 
409 			rtld_flags |= RT_FL_MEMRESV;
410 			features |= CONF_MEMRESV;
411 		}
412 	}
413 
414 	return (features);
415 }
416 
417 /*
418  * Determine whether the given file exists in the configuration file.
419  */
420 Rtc_obj *
421 elf_config_ent(const char *name, Word hash, int id, const char **alternate)
422 {
423 	Word		bkt, ndx;
424 	const char	*str;
425 	Rtc_obj		*obj;
426 
427 	bkt = hash % config->c_hashtbl[0];
428 	ndx = config->c_hashtbl[2 + bkt];
429 
430 	while (ndx) {
431 		obj = config->c_objtbl + ndx;
432 		str = config->c_strtbl + obj->co_name;
433 
434 		if ((obj->co_hash != hash) || (strcmp(name, str) != 0) ||
435 		    (id && (id != obj->co_id))) {
436 			ndx = config->c_hashchain[ndx];
437 			continue;
438 		}
439 
440 		if ((obj->co_flags & RTC_OBJ_ALTER) && alternate)
441 			*alternate = config->c_strtbl + obj->co_alter;
442 
443 		return (obj);
444 	}
445 	return (0);
446 }
447 
448 /*
449  * Determine whether a filter and filtee string pair exists in the configuration
450  * file.  If so, return the cached filtees that are associated with this pair as
451  * a Pnode list.
452  */
453 Pnode *
454 elf_config_flt(Lm_list *lml, const char *filter, const char *string)
455 {
456 	Rtc_fltr *	fltrtbl;
457 	Pnode *		pnp = 0, *npnp, *opnp = 0;
458 
459 	for (fltrtbl = (Rtc_fltr *)config->c_fltr; fltrtbl->fr_filter;
460 	    fltrtbl++) {
461 		Rtc_flte	*fltetbl;
462 		const char	*fltr, *str;
463 
464 		fltr = config->c_strtbl + fltrtbl->fr_filter;
465 		str = config->c_strtbl + fltrtbl->fr_string;
466 		if (strcmp(filter, fltr) || strcmp(string, str))
467 			continue;
468 
469 		/*
470 		 * Create a pnode list for each filtee associated with this
471 		 * filter/filtee string pair.  Note, no expansion of filtee
472 		 * entries is called for, as any original expansion would have
473 		 * been carried out before they were recorded in the
474 		 * configuration file.
475 		 */
476 		/* LINTED */
477 		for (fltetbl = (Rtc_flte *)((char *)config->c_flte +
478 		    fltrtbl->fr_filtee); fltetbl->fe_filtee; fltetbl++) {
479 			const char	*flte;
480 
481 			flte = config->c_strtbl + fltetbl->fe_filtee;
482 
483 			if (((npnp = calloc(1, sizeof (Pnode))) == 0) ||
484 			    ((npnp->p_name = strdup(flte)) == 0))
485 				return (0);
486 
487 			DBG_CALL(Dbg_file_filter(lml, fltr, flte, 1));
488 
489 			if (opnp == 0)
490 				pnp = npnp;
491 			else
492 				opnp->p_next = npnp;
493 
494 			npnp->p_len = strlen(flte) + 1;
495 			npnp->p_orig = LA_SER_CONFIG;
496 
497 			opnp = npnp;
498 		}
499 		return (pnp);
500 	}
501 	return (0);
502 }
503