xref: /illumos-gate/usr/src/cmd/sgs/crle/common/depend.c (revision 57ef7aa9)
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 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include	<sys/types.h>
27 #include	<stdio.h>
28 #include	<errno.h>
29 #include	<unistd.h>
30 #include	<string.h>
31 #include	<wait.h>
32 #include	<limits.h>
33 #include	<gelf.h>
34 #include	"machdep.h"
35 #include	"sgs.h"
36 #include	"conv.h"
37 #include	"_crle.h"
38 #include	"msg.h"
39 
40 /*
41  * Establish an association between a filter and filtee.  Both the filter and
42  * filtee already exist in the internal hash table, since auditing registers
43  * objects (la_objopen()) before it registers filters (la_objfilter()).
44  */
45 static int
46 filter(Crle_desc *crle, const char *filter, const char *str, const char *filtee)
47 {
48 	Hash_ent	*fltrent, *flteent;
49 	Flt_desc	*flt;
50 	Aliste		idx;
51 
52 	/*
53 	 * Locate the filter.  Mark the underlying object as the filter to
54 	 * reflect that no matter how it is referenced, it's a filter.
55 	 */
56 	if ((fltrent = get_hash(crle->c_strtbl, (Addr)filter, 0,
57 	    HASH_FND_ENT)) == NULL)
58 		return (1);
59 	if ((fltrent = get_hash(crle->c_strtbl, (Addr)fltrent->e_obj->o_path, 0,
60 	    HASH_FND_ENT)) == NULL)
61 		return (1);
62 	fltrent->e_obj->o_flags |= RTC_OBJ_FILTER;
63 
64 	/*
65 	 * Locate the filtee.  Mark the referencing object as the filtee, as
66 	 * this is the object referenced by the filter.
67 	 */
68 	if ((flteent = get_hash(crle->c_strtbl, (Addr)filtee, 0,
69 	    HASH_FND_ENT)) == NULL)
70 		return (1);
71 	flteent->e_flags |= RTC_OBJ_FILTEE;
72 
73 	/*
74 	 * Traverse the filter list using the filters real name.  If ld.so.1
75 	 * inspects the resulting configuration file for filters, it's the
76 	 * objects real name that will be used (PATHNAME()).
77 	 */
78 	for (APLIST_TRAVERSE(crle->c_flt, idx, flt)) {
79 		/*
80 		 * Determine whether this filter and filtee string pair already
81 		 * exist.
82 		 */
83 		if ((strcmp(flt->f_fent->e_obj->o_path,
84 		    fltrent->e_obj->o_path) != 0) &&
85 		    (strcmp(flt->f_str, str) != 0))
86 			continue;
87 
88 		/*
89 		 * Add this filtee additional association.
90 		 */
91 		if (aplist_append(&(flt->f_filtee), flteent,
92 		    AL_CNT_CRLE) == NULL)
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))) == NULL) ||
103 	    ((flt->f_strsz = strlen(str) + 1) == 0) ||
104 	    ((flt->f_str = malloc(flt->f_strsz)) == NULL)) {
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 ((aplist_append(&(crle->c_flt), flt, AL_CNT_CRLE) == NULL) ||
111 	    (aplist_append(&(flt->f_filtee), flteent, AL_CNT_CRLE) == NULL))
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  */
128 int
129 depend(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 = NULL;
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