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 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
depend(Crle_desc * crle,const char * name,Half flags,GElf_Ehdr * ehdr)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