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