]> git.cworth.org Git - fips/blob - execute.c
glxwrap: Initialize fips_dispatch when glxMakeContextCurrent is called
[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 "fips.h"
23
24 #include <errno.h>
25
26 #include <unistd.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <sys/wait.h>
30
31 #include <talloc.h>
32
33 #include <linux/limits.h>
34
35 #include <fcntl.h>
36 #include <gelf.h>
37
38 #include "execute.h"
39
40 /* Terminate a string representing a filename at the final '/' to
41  * eliminate the final filename component, (leaving only the directory
42  * portions of the original path).
43  *
44  * Notes: A path containing no '/' character will not be modified.
45  *        A path consisting only of "/" will not be modified.
46  */
47 static void
48 chop_trailing_path_component (char *path)
49 {
50         char *slash;
51
52         slash = strrchr (path, '/');
53
54         if (slash == NULL)
55                 return;
56
57         if (slash == path)
58                 return;
59
60         *slash = '\0';
61 }
62
63 /* Find the absolute path of the currently executing binary.
64  *
65  * Returns: a string talloc'ed to 'ctx'
66  */
67 static char *
68 get_bin_name (void *ctx)
69 {
70         const char *link = "/proc/self/exe";
71         char *name;
72
73         /* Yes, PATH_MAX is cheesy. I would have preferred to have
74          * used lstat and read the resulting st_size, but everytime I
75          * did that with /proc/self/exe I got a value of 0, (whereas
76          * with a "real" symbolic link I make myself I get the length
77          * of the filename being linked to). Go figure. */
78         int name_len = PATH_MAX + 1;
79
80         name = talloc_size (ctx, name_len);
81         if (name == NULL) {
82                 fprintf (stderr, "Out of memory.\n");
83                 exit (1);
84         }
85
86         name_len = readlink (link, name, name_len - 1);
87         if (name_len < 0) {
88                 fprintf (stderr, "Failed to readlink %s: %s\n", link,
89                          strerror (errno));
90                 exit (1);
91         }
92
93         name[name_len] = '\0';
94
95         return name;
96 }
97
98 /* Does path exist? */
99 static int
100 exists (const char *path)
101 {
102         struct stat st;
103         int err;
104
105         err = stat (path, &st);
106
107         /* Failed to stat. It either doesn't exist, or might as well not. */
108         if (err == -1)
109                 return 0;
110
111         return 1;
112 }
113
114 /* Given a program name, search the PATH environment variable and
115  * return the first absolute path to 'program'.
116  *
117  * Returns: A string talloc'ed to 'ctx'.
118  *
119  * Note: This function aborts the current program if 'program' cannot
120  * be located by searching PATH.
121  */
122 static char *
123 search_path_for_program (void *ctx, const char *program)
124 {
125         char *orig_path, *path, *colon, *dir, *candidate;
126         void *local = talloc_new (ctx);
127
128         /* If the program name already contains a slash, then this is
129          * an absolute (or relative) path. Either way, we don't search
130          * PATH, since we can directly open this filename. */
131         if (strchr (program, '/'))
132             return talloc_strdup (ctx, program);
133
134         orig_path = path = getenv ("PATH");
135
136         while (*path) {
137                 colon = strchr (path, ':');
138
139                 if (colon) {
140                         dir = talloc_strndup (local, path, colon - path);
141                         path = colon + 1;
142                 } else {
143                         dir = path;
144                         path = path + strlen (path);
145                 }
146
147                 candidate = talloc_asprintf(local, "%s/%s", dir, program);
148
149                 if (exists (candidate)) {
150                         talloc_steal (ctx, candidate);
151                         talloc_free (local);
152                         return candidate;
153                 } else {
154                         talloc_free (candidate);
155                 }
156         }
157
158         fprintf (stderr, "Cannot find program %s (looked in %s)\n",
159                  program, orig_path);
160         exit (1);
161 }
162
163 /* Is the given elf program 32 or 64 bit?
164  *
165  * Note: This function aborts the current program if 'program' cannot
166  * be opened as a valid ELF file. */
167 static int
168 elf_bits (const char *program)
169 {
170         Elf *elf;
171         GElf_Ehdr ehdr;
172         int fd, class;
173         void *local = talloc_new (NULL);
174         char *absolute_program = search_path_for_program (local, program);
175
176         fd = open (absolute_program, O_RDONLY, 0);
177         if (fd < 0) {
178                 fprintf (stderr, "Failed to open %s: %s\n", absolute_program,
179                          strerror (errno));
180                 exit (1);
181         }
182
183         if (elf_version (EV_CURRENT ) == EV_NONE) {
184                 fprintf (stderr, "Failed to initialize elf library: %s\n",
185                          elf_errmsg (-1));
186                 exit (1);
187         }
188
189         elf = elf_begin (fd, ELF_C_READ, NULL);
190         if (elf == NULL) {
191                 fprintf (stderr, "Call to elf_begin on %s failed: %s\n",
192                          absolute_program, elf_errmsg(-1));
193                 exit (1);
194         }
195
196         if (elf_kind (elf) != ELF_K_ELF) {
197                 fprintf (stderr, "Not an ELF object: %s\n", absolute_program);
198                 exit (1);
199         }
200
201         if (gelf_getehdr (elf, &ehdr) == NULL) {
202                 fprintf (stderr, "getehdr on %s failed: %s\n",
203                          absolute_program, elf_errmsg (-1));
204                 exit (1);
205         }
206
207         class = gelf_getclass (elf);
208
209         if (class == ELFCLASSNONE) {
210                 fprintf (stderr, "getclass on %s failed: %s\n",
211                          absolute_program, elf_errmsg (-1));
212                 exit (1);
213         }
214
215         talloc_free (local);
216
217         if (class == ELFCLASS32)
218                 return 32;
219         else
220                 return 64;
221
222 }
223
224 /* Find the appropriate path to the libfips wrapper.
225  *
226  * This involves, first, examining the elf header of the 'program'
227  * binary to be executed to know whether we should look for
228  * libfips-32.so or libfips-64.so.
229  *
230  * Next, we find the absolute patch containing the library as follows:
231  *
232  *   1. Look in same directory as current executable image
233  *
234  *      This is to support running from the source directory, without
235  *      having installed anything.
236  *
237  *   2. Look in relative path from $(foo)/$(bindir) to
238  *      $(foo)/$(libdir)/fips based on $(foo) from current executable
239  *      image and configured $(bindir) and $(libdir).
240  *
241  *      We do this rather than looking directly at the configured
242  *      $(libdir) to support cases where the application may have been
243  *      moved after being installed, (in particular, we want to be
244  *      particularly careful not to mix one program with a different
245  *      wrapper---so this "nearest search" should most often be
246  *      correct.
247  *
248  * Returns: a string talloc'ed to 'ctx'
249  */
250 static char *
251 find_libfips_path (void *ctx, const char *program)
252 {
253         char *bin_path, *library, *lib_path;
254         int bits;
255
256         bits = elf_bits (program);
257
258         library = talloc_asprintf(ctx, "libfips-%d.so", bits);
259
260         bin_path = get_bin_name (ctx);
261
262         chop_trailing_path_component (bin_path);
263
264         lib_path = talloc_asprintf(ctx, "%s/%s", bin_path, library);
265
266         if (exists (lib_path))
267                 return lib_path;
268
269         talloc_free (lib_path);
270
271         lib_path = talloc_asprintf(ctx, "%s/" BINDIR_TO_LIBFIPSDIR "/%s",
272                                    bin_path, library);
273
274         if (exists (lib_path))
275                 return lib_path;
276
277         fprintf (stderr, "Error: Failed to find library %s.\n", library);
278         fprintf (stderr, "Looked in both:\n"
279                  "\t%s\n"
280                  "and\n"
281                  "\t%s/" BINDIR_TO_LIBFIPSDIR "\n", bin_path, bin_path);
282
283         fprintf(stderr, "\nIt's possible fips was not compiled with support for %d-bit applications.\n", bits);
284         fprintf(stderr, "Perhaps you need to install gcc-multilib and re-compile fips?\n");
285         exit (1);
286 }
287
288 int
289 execute_with_fips_preload (int argc, char * const argv[])
290 {
291         void *ctx = talloc_new (NULL);
292         char *lib_path;
293         char *ld_preload_value;
294         char **execvp_args;
295         int i;
296
297         execvp_args = malloc((argc + 1) * sizeof(char *));
298         if (execvp_args == NULL) {
299                 fprintf (stderr, "Out of memory,\n");
300                 return 1;
301         }
302
303         for (i = 0; i < argc; i++) {
304                 execvp_args[i] = argv[i];
305         }
306
307         /* execvp needs final NULL */
308         execvp_args[i] = NULL;
309
310         lib_path = find_libfips_path (ctx, argv[0]);
311
312         ld_preload_value = getenv ("LD_PRELOAD");
313
314         if (ld_preload_value) {
315                 ld_preload_value = talloc_asprintf(ctx, "%s:%s",
316                                                    ld_preload_value,
317                                                    lib_path);
318         } else {
319                 ld_preload_value = lib_path;
320         }
321
322         setenv ("LD_PRELOAD", ld_preload_value, 1);
323
324         talloc_free (ctx);
325                 
326         execvp (argv[0], argv);
327         fprintf (stderr, "Failed to execute:");
328         for (i = 0; argv[i]; i++) {
329                 fprintf (stderr, " %s", argv[i]);
330         }
331         fprintf (stderr, "\n");
332         exit (1);
333 }