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