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
filter(Crle_desc * crle,const char * filter,const char * str,const char * filtee)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 free(flt);
109 return (1);
110 }
111 if ((aplist_append(&(crle->c_flt), flt, AL_CNT_CRLE) == NULL) ||
112 (aplist_append(&(flt->f_filtee), flteent, AL_CNT_CRLE) == NULL))
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
depend(Crle_desc * crle,const char * name,Half flags,GElf_Ehdr * ehdr)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 = NULL;
143 } else {
144 exename = conv_lddstub(M_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