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