]> git.cworth.org Git - fips/blob - execute.c
Append to, rather than replace, the LD_PRELOAD value.
[fips] / execute.c
1 /* Copyright © 2013, Intel Corporation
2  *
3  * Permission is hereby granted, free of charge, to any person obtaining a copy
4  * of this software and associated documentation files (the "Software"), to deal
5  * in the Software without restriction, including without limitation the rights
6  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7  * copies of the Software, and to permit persons to whom the Software is
8  * furnished to do so, subject to the following conditions:
9  *
10  * The above copyright notice and this permission notice shall be included in
11  * all copies or substantial portions of the Software.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19  * THE SOFTWARE.
20  */
21
22 #include "config.h"
23
24 #include <stdio.h>
25 #include <stdlib.h>
26
27 #include <string.h>
28 #include <errno.h>
29
30 #include <unistd.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <sys/wait.h>
34
35 #include <talloc.h>
36
37 #include <linux/limits.h>
38
39 #include <fcntl.h>
40 #include <gelf.h>
41
42 #include "execute.h"
43
44 /* Terminate a string representing a filename at the final '/' to
45  * eliminate the final filename component, (leaving only the directory
46  * portions of the original path).
47  *
48  * Notes: A path containing no '/' character will not be modified.
49  *        A path consisting only of "/" will not be modified.
50  */
51 static void
52 chop_trailing_path_component (char *path)
53 {
54         char *slash;
55
56         slash = strrchr (path, '/');
57
58         if (slash == NULL)
59                 return;
60
61         if (slash == path)
62                 return;
63
64         *slash = '\0';
65 }
66
67 /* Find the absolute path of the currently executing binary.
68  *
69  * Returns: a string talloc'ed to 'ctx'
70  */
71 static char *
72 get_bin_name (void *ctx)
73 {
74         const char *link = "/proc/self/exe";
75         char *name;
76
77         /* Yes, PATH_MAX is cheesy. I would have preferred to have
78          * used lstat and read the resulting st_size, but everytime I
79          * did that with /proc/self/exe I got a value of 0, (whereas
80          * with a "real" symbolic link I make myself I get the length
81          * of the filename being linked to). Go figure. */
82         int name_len = PATH_MAX + 1;
83
84         name = talloc_size (ctx, name_len - 1);
85         if (name == NULL) {
86                 fprintf (stderr, "Out of memory.\n");
87                 exit (1);
88         }
89
90         name_len = readlink (link, name, name_len);
91         if (name_len < 0) {
92                 fprintf (stderr, "Failed to readlink %s: %s\n", link,
93                          strerror (errno));
94                 exit (1);
95         }
96
97         name[name_len + 1] = '\0';
98
99         return name;
100 }
101
102 /* Does path exist? */
103 static int
104 exists (const char *path)
105 {
106         struct stat st;
107         int err;
108
109         err = stat (path, &st);
110
111         /* Failed to stat. It either doesn't exist, or might as well not. */
112         if (err == -1)
113                 return 0;
114
115         return 1;
116 }
117
118 /* Given a program name, search the PATH environment variable and
119  * return the first absolute path to 'program'.
120  *
121  * Returns: A string talloc'ed to 'ctx'.
122  *
123  * Note: This function aborts the current program if 'program' cannot
124  * be located by searching PATH.
125  */
126 static char *
127 search_path_for_program (void *ctx, const char *program)
128 {
129         char *orig_path, *path, *colon, *dir, *candidate;
130         void *local = talloc_new (ctx);
131
132         /* If the program name already contains a slash, then this is
133          * an absolute (or relative) path. Either way, we don't search
134          * PATH, since we can directly open this filename. */
135         if (strchr (program, '/'))
136             return talloc_strdup (ctx, program);
137
138         orig_path = path = getenv ("PATH");
139
140         while (*path) {
141                 colon = strchr (path, ':');
142
143                 if (colon) {
144                         dir = talloc_strndup (local, path, colon - path);
145                         path = colon + 1;
146                 } else {
147                         dir = path;
148                         path = path + strlen (path);
149                 }
150
151                 candidate = talloc_asprintf(local, "%s/%s", dir, program);
152
153                 if (exists (candidate)) {
154                         talloc_steal (ctx, candidate);
155                         talloc_free (local);
156                         return candidate;
157                 } else {
158                         talloc_free (candidate);
159                 }
160         }
161
162         fprintf (stderr, "Cannot find program %s (looked in %s)\n",
163                  program, orig_path);
164         exit (1);
165 }
166
167 /* Is the given elf program 32 or 64 bit?
168  *
169  * Note: This function aborts the current program if 'program' cannot
170  * be opened as a valid ELF file. */
171 static int
172 elf_bits (const char *program)
173 {
174         Elf *elf;
175         GElf_Ehdr ehdr;
176         int fd, class;
177         void *local = talloc_new (NULL);
178         char *absolute_program = search_path_for_program (local, program);
179
180         fd = open (absolute_program, O_RDONLY, 0);
181         if (fd < 0) {
182                 fprintf (stderr, "Failed to open %s: %s\n", absolute_program,
183                          strerror (errno));
184                 exit (1);
185         }
186
187         if (elf_version (EV_CURRENT ) == EV_NONE) {
188                 fprintf (stderr, "Failed to initialize elf library: %s\n",
189                          elf_errmsg (-1));
190                 exit (1);
191         }
192
193         elf = elf_begin (fd, ELF_C_READ, NULL);
194         if (elf == NULL) {
195                 fprintf (stderr, "Call to elf_begin on %s failed: %s\n",
196                          absolute_program, elf_errmsg(-1));
197                 exit (1);
198         }
199
200         if (elf_kind (elf) != ELF_K_ELF) {
201                 fprintf (stderr, "Not an ELF object: %s\n", absolute_program);
202                 exit (1);
203         }
204
205         if (gelf_getehdr (elf, &ehdr) == NULL) {
206                 fprintf (stderr, "getehdr on %s failed: %s\n",
207                          absolute_program, elf_errmsg (-1));
208                 exit (1);
209         }
210
211         class = gelf_getclass (elf);
212
213         if (class == ELFCLASSNONE) {
214                 fprintf (stderr, "getclass on %s failed: %s\n",
215                          absolute_program, elf_errmsg (-1));
216                 exit (1);
217         }
218
219         talloc_free (local);
220
221         if (class == ELFCLASS32)
222                 return 32;
223         else
224                 return 64;
225
226 }
227
228 /* Find the appropriate path to the libfips wrapper.
229  *
230  * This involves, first, examining the elf header of the 'program'
231  * binary to be executed to know whether we should look for
232  * libfips-32.so or libfips-64.so.
233  *
234  * Next, we find the absolute patch containing the library as follows:
235  *
236  *   1. Look in same directory as current executable image
237  *
238  *      This is to support running from the source directory, without
239  *      having installed anything.
240  *
241  *   2. Look in relative path from $(foo)/$(bindir) to
242  *      $(foo)/$(libdir)/fips based on $(foo) from current executable
243  *      image and configured $(bindir) and $(libdir).
244  *
245  *      We do this rather than looking directly at the configured
246  *      $(libdir) to support cases where the application may have been
247  *      moved after being installed, (in particular, we want to be
248  *      particularly careful not to mix one program with a different
249  *      wrapper---so this "nearest search" should most often be
250  *      correct.
251  *
252  * Returns: a string talloc'ed to 'ctx'
253  */
254 static char *
255 find_libfips_path (void *ctx, const char *program)
256 {
257         char *bin_path, *library, *lib_path;
258         int bits;
259
260         bits = elf_bits (program);
261
262         library = talloc_asprintf(ctx, "libfips-%d.so", bits);
263
264         bin_path = get_bin_name (ctx);
265
266         chop_trailing_path_component (bin_path);
267
268         lib_path = talloc_asprintf(ctx, "%s/%s", bin_path, library);
269
270         if (exists (lib_path))
271                 return lib_path;
272
273         talloc_free (lib_path);
274
275         lib_path = talloc_asprintf(ctx, "%s/" BINDIR_TO_LIBFIPSDIR "/%s",
276                                    bin_path, library);
277
278         if (exists (lib_path))
279                 return lib_path;
280
281         fprintf (stderr, "Error: Failed to find library %s.\n", library);
282         fprintf (stderr, "Looked in both:\n"
283                  "\t%s\n"
284                  "and\n"
285                  "\t%s/" BINDIR_TO_LIBFIPSDIR "\n", bin_path, bin_path);
286         exit (1);
287 }
288
289 /* After forking, set LD_PRELOAD to preload libfips-{32,64}.so within
290  * child environment, then exec given arguments.
291  */
292 static int
293 fork_exec_with_fips_preload_and_wait (char * const argv[])
294 {
295         pid_t pid;
296         int i, status;
297
298         pid = fork ();
299
300         /* Child */
301         if (pid == 0) {
302                 void *ctx = talloc_new (NULL);
303                 char *lib_path;
304                 char *ld_preload_value;
305
306                 lib_path = find_libfips_path (ctx, argv[0]);
307
308                 ld_preload_value = getenv ("LD_PRELOAD");
309
310                 if (ld_preload_value) {
311                         ld_preload_value = talloc_asprintf(ctx, "%s:%s",
312                                                            ld_preload_value,
313                                                            lib_path);
314                 } else {
315                         ld_preload_value = lib_path;
316                 }
317
318                 setenv ("LD_PRELOAD", ld_preload_value, 1);
319
320                 talloc_free (ctx);
321                 
322                 execvp (argv[0], argv);
323                 fprintf (stderr, "Failed to execute:");
324                 for (i = 0; argv[i]; i++) {
325                         fprintf (stderr, " %s", argv[i]);
326                 }
327                 fprintf (stderr, "\n");
328                 exit (1);
329         }
330
331         /* Parent */
332         waitpid (pid, &status, 0);
333         if (WIFEXITED (status)) {
334                 return (WEXITSTATUS (status));
335         }
336         if (WIFSIGNALED (status)) {
337                 fprintf (stderr, "Child terminated by signal %d\n",
338                          WTERMSIG (status));
339         }
340         return 1;
341 }
342
343 int
344 execute_with_fips_preload (int argc, char * const argv[])
345 {
346         char **execvp_args;
347         int i;
348
349         execvp_args = malloc((argc + 1) * sizeof(char *));
350         if (execvp_args == NULL) {
351                 fprintf (stderr, "Out of memory,\n");
352                 return 1;
353         }
354
355         for (i = 0; i < argc; i++) {
356                 execvp_args[i] = argv[i];
357         }
358
359         /* execvp needs final NULL */
360         execvp_args[i] = NULL;
361
362         return fork_exec_with_fips_preload_and_wait (execvp_args);
363 }