depend.c revision 1976:f0691a145b7e
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 * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25#pragma ident	"%Z%%M%	%I%	%E% SMI"
26
27#include	<sys/types.h>
28#include	<stdio.h>
29#include	<errno.h>
30#include	<unistd.h>
31#include	<string.h>
32#include	<wait.h>
33#include	<limits.h>
34#include	<gelf.h>
35#include	"machdep.h"
36#include	"sgs.h"
37#include	"conv.h"
38#include	"_crle.h"
39#include	"msg.h"
40
41/*
42 * Establish an association between a filter and filtee.  Both the filter and
43 * filtee already exist in the internal hash table, since auditing registers
44 * objects (la_objopen()) before it registers filters (la_objfilter()).
45 */
46static int
47filter(Crle_desc *crle, const char *filter, const char *str, const char *filtee)
48{
49	Hash_ent *	fltrent, * flteent;
50	Flt_desc *	flt;
51	Listnode *	lnp;
52
53	/*
54	 * Locate the filter.  Mark the underlying object as the filter to
55	 * reflect that no matter how it is referenced, it's a filter.
56	 */
57	if ((fltrent = get_hash(crle->c_strtbl, (Addr)filter, 0,
58	    HASH_FND_ENT)) == 0)
59		return (1);
60	if ((fltrent = get_hash(crle->c_strtbl, (Addr)fltrent->e_obj->o_path, 0,
61	    HASH_FND_ENT)) == 0)
62		return (1);
63	fltrent->e_obj->o_flags |= RTC_OBJ_FILTER;
64
65	/*
66	 * Locate the filtee.  Mark the referencing object as the filtee, as
67	 * this is the object referenced by the filter.
68	 */
69	if ((flteent = get_hash(crle->c_strtbl, (Addr)filtee, 0,
70	    HASH_FND_ENT)) == 0)
71		return (1);
72	flteent->e_flags |= RTC_OBJ_FILTEE;
73
74	/*
75	 * Traverse the filter list using the filters real name.  If ld.so.1
76	 * inspects the resulting configuration file for filters, it's the
77	 * objects real name that will be used (PATHNAME()).
78	 */
79	for (LIST_TRAVERSE(&(crle->c_flt), lnp, flt)) {
80		/*
81		 * Determine whether this filter and filtee string pair already
82		 * exist.
83		 */
84		if ((strcmp(flt->f_fent->e_obj->o_path,
85		    fltrent->e_obj->o_path) != 0) &&
86		    (strcmp(flt->f_str, str) != 0))
87			continue;
88
89		/*
90		 * Add this filtee additional association.
91		 */
92		if (list_append(&(flt->f_filtee), flteent) == 0)
93			return (1);
94
95		crle->c_fltenum++;
96		return (0);
97	}
98
99	/*
100	 * This is a new filter descriptor.  Add this new filtee association.
101	 */
102	if (((flt = malloc(sizeof (Flt_desc))) == 0) ||
103	    ((flt->f_strsz = strlen(str) + 1) == 0) ||
104	    ((flt->f_str = malloc(flt->f_strsz)) == 0)) {
105		int err = errno;
106		(void) fprintf(stderr, MSG_INTL(MSG_SYS_MALLOC),
107		    crle->c_name, strerror(err));
108		return (1);
109	}
110	if ((list_append(&(crle->c_flt), flt) == 0) ||
111	    (list_append(&(flt->f_filtee), flteent) == 0))
112		return (1);
113
114	flt->f_fent = fltrent;
115	(void) memcpy((void *)flt->f_str, (void *)str, flt->f_strsz);
116	crle->c_strsize += flt->f_strsz;
117	crle->c_fltrnum += 1;
118	crle->c_fltenum += 2;		/* Account for null filtee desc. */
119
120	return (0);
121}
122
123/*
124 * Establish the dependencies of an ELF object and add them to the internal
125 * configuration information. This information is gathered by using libcrle.so.1
126 * as an audit library - this is akin to using ldd(1) only simpler.
127 */
128int
129depend(Crle_desc *crle, const char *name, Half flags, GElf_Ehdr *ehdr)
130{
131	const char	*exename;
132	const char	*preload;
133	int		fildes[2], pid;
134
135	/*
136	 * If we're dealing with a dynamic executable we'll execute it,
137	 * otherwise we'll preload the shared object with one of the lddstub's.
138	 */
139	if (ehdr->e_type == ET_EXEC) {
140		exename = name;
141		preload = 0;
142	} else {
143		exename = conv_lddstub(M_CLASS);
144		preload = name;
145	}
146
147	/*
148	 * Set up a pipe through which the audit library will write the
149	 * dependencies.
150	 */
151	if (pipe(fildes) == -1) {
152		int err = errno;
153		(void) fprintf(stderr, MSG_INTL(MSG_SYS_PIPE),
154		    crle->c_name, strerror(err));
155		return (1);
156	}
157
158	/*
159	 * Fork ourselves to run our executable and collect its dependencies.
160	 */
161	if ((pid = fork()) == -1) {
162		int err = errno;
163		(void) fprintf(stderr, MSG_INTL(MSG_SYS_FORK),
164		    crle->c_name, strerror(err));
165		return (1);
166	}
167
168	if (pid) {
169		/*
170		 * Parent. Read each dependency from the audit library. The read
171		 * side of the pipe is attached to stdio to make obtaining the
172		 * individual dependencies easier.
173		 */
174		int	error = 0, status;
175		FILE	*fd;
176		char	buffer[PATH_MAX];
177
178		(void) close(fildes[1]);
179		if ((fd = fdopen(fildes[0], MSG_ORIG(MSG_STR_READ))) != NULL) {
180			char	*str;
181
182			while (fgets(buffer, PATH_MAX, fd) != NULL) {
183				/*
184				 * Make sure we recognize the message, remove
185				 * the newline (which allowed fgets() use) and
186				 * register the name;
187				 */
188				if (strncmp(MSG_ORIG(MSG_AUD_PRF), buffer,
189				    MSG_AUD_PRF_SIZE))
190					continue;
191
192				str = strrchr(buffer, '\n');
193				*str = '\0';
194				str = buffer + MSG_AUD_PRF_SIZE;
195
196				if (strncmp(MSG_ORIG(MSG_AUD_DEPEND),
197				    str, MSG_AUD_DEPEND_SIZE) == 0) {
198					/*
199					 * Process any dependencies.
200					 */
201					str += MSG_AUD_DEPEND_SIZE;
202
203					if ((error = inspect(crle, str,
204					    (flags & ~RTC_OBJ_GROUP))) != 0)
205						break;
206
207				} else if (strncmp(MSG_ORIG(MSG_AUD_FILTER),
208				    str, MSG_AUD_FILTER_SIZE) == 0) {
209					char	*_flt, *_str;
210
211					/*
212					 * Process any filters.
213					 */
214					_flt = str += MSG_AUD_FILTER_SIZE;
215					_str = strchr(str, ':');
216					*_str++ = '\0'; str = _str++;
217					str = strrchr(str, ')');
218					*str++ = '\0'; str++;
219					if ((error = filter(crle, _flt, _str,
220					    str)) != 0)
221						break;
222				}
223			}
224		} else
225			error = errno;
226
227		while (wait(&status) != pid)
228			;
229		if (status) {
230			if (WIFSIGNALED(status)) {
231				(void) fprintf(stderr,
232				    MSG_INTL(MSG_SYS_EXEC), crle->c_name,
233				    exename, (WSIGMASK & status),
234				    ((status & WCOREFLG) ?
235				    MSG_INTL(MSG_SYS_CORE) :
236				    MSG_ORIG(MSG_STR_EMPTY)));
237			}
238			error = status;
239		}
240		(void) fclose(fd);
241
242		return (error);
243	} else {
244		char	efds[MSG_ENV_AUD_FD_SIZE + 10];
245		char	epld[PATH_MAX];
246		char	eldf[PATH_MAX];
247
248		(void) close(fildes[0]);
249
250		/*
251		 * Child. Set up environment variables to enable and identify
252		 * auditing.  Initialize CRLE_FD and LD_FLAGS strings.
253		 */
254		(void) snprintf(efds, (MSG_ENV_AUD_FD_SIZE + 10),
255		    MSG_ORIG(MSG_ENV_AUD_FD), fildes[1]);
256		(void) snprintf(eldf, PATH_MAX, MSG_ORIG(MSG_ENV_LD_FLAGS));
257
258		/*
259		 * If asked to dump a group of dependencies make sure any
260		 * lazily-loaded objects get processed - (append loadavail to
261		 * LD_FLAGS=confgen).
262		 */
263		if (flags & RTC_OBJ_GROUP)
264			(void) strcat(eldf, MSG_ORIG(MSG_LDFLG_LOADAVAIL));
265
266		/*
267		 * Put LD_PRELOAD= in the environment if necessary.
268		 */
269		if (preload) {
270			(void) snprintf(epld, PATH_MAX,
271			    MSG_ORIG(MSG_ENV_LD_PRELOAD), preload);
272		}
273
274		/*
275		 * Put strings in the environment for exec().
276		 * NOTE, use of automatic variables for construction of the
277		 * environment variables is legitimate here, as they are local
278		 * to the child process and are established solely for exec().
279		 */
280		if ((putenv(efds) != 0) || (putenv(crle->c_audit) != 0) ||
281		    (putenv(eldf) != 0) || (preload && (putenv(epld) != 0))) {
282			int err = errno;
283			(void) fprintf(stderr, MSG_INTL(MSG_SYS_PUTENV),
284			    crle->c_name, strerror(err));
285			return (1);
286		}
287
288		if (execlp(exename, exename, 0) == -1) {
289			_exit(errno);
290			/* NOTREACHED */
291		}
292	}
293	return (0);
294}
295