X-Git-Url: https://git.cworth.org/git?a=blobdiff_plain;f=execute.c;h=b102a4a2b4b4945e8cc20dc1cfeadeb43f90f789;hb=1f4014b53a4dc77768f3998a6fd3b67024431fa3;hp=9a3d1f75059b112c3e961da9680e81a2f0d4c5fc;hpb=07d2b55d88742b0ca38507daae7101619f0b96d5;p=fips diff --git a/execute.c b/execute.c index 9a3d1f7..b102a4a 100644 --- a/execute.c +++ b/execute.c @@ -19,15 +19,158 @@ * THE SOFTWARE. */ +#include "config.h" + #include #include +#include +#include + #include #include +#include #include +#include + +#include + +#include "execute.h" + +/* Terminate a string representing a filename at the final '/' to + * eliminate the final filename component, (leaving only the directory + * portions of the original path). + * + * Notes: A path containing no '/' character will not be modified. + * A path consisting only of "/" will not be modified. + */ +static void +chop_trailing_path_component (char *path) +{ + char *slash; + + slash = strrchr (path, '/'); + + if (slash == NULL) + return; + + if (slash == path) + return; + + *slash = '\0'; +} + +/* Find the absolute path of the currently executing binary. + * + * Returns: a string talloc'ed to 'ctx' + */ +static char * +get_bin_name (void *ctx) +{ + const char *link = "/proc/self/exe"; + char *name; + + /* Yes, PATH_MAX is cheesy. I would have preferred to have + * used lstat and read the resulting st_size, but everytime I + * did that with /proc/self/exe I got a value of 0, (whereas + * with a "real" symbolic link I make myself I get the length + * of the filename being linked to). Go figure. */ + int name_len = PATH_MAX + 1; + + name = talloc_size (ctx, name_len - 1); + if (name == NULL) { + fprintf (stderr, "Out of memory.\n"); + exit (1); + } + + name_len = readlink (link, name, name_len); + if (name_len < 0) { + fprintf (stderr, "Failed to readlink %s: %s\n", link, + strerror (errno)); + exit (1); + } + + name[name_len + 1] = '\0'; + + return name; +} + +/* Does path exist? */ static int -fork_exec_and_wait (char * const argv[]) +exists (const char *path) +{ + struct stat st; + int err; + + err = stat (path, &st); + + /* Failed to stat. It either doesn't exist, or might as well not. */ + if (err == -1) + return 0; + + return 1; +} + +/* Given "library" filename resolve it to an absolute path to an + * existing file as follows: + * + * 1. Look in same directory as current executable image + * + * This is to support running from the source directory, without + * having installed anything. + * + * 2. Look in relative path from $(foo)/$(bindir) to + * $(foo)/$(libdir)/fips based on $(foo) from current executable + * image and configured $(bindir) and $(libdir). + * + * We do this rather than looking directly at the configured + * $(libdir) to support cases where the application may have been + * moved after being installed, (in particular, we want to be + * particularly careful not to mix one program with a different + * wrapper---so this "nearest search" should most often be + * correct. + * + * Returns: a string talloc'ed to 'ctx' + */ +static char * +resolve_path (void *ctx, const char *library) +{ + char *bin_path, *lib_path; + + bin_path = get_bin_name (ctx); + + chop_trailing_path_component (bin_path); + + lib_path = talloc_asprintf(ctx, "%s/%s", bin_path, library); + + if (exists (lib_path)) + return lib_path; + + talloc_free (lib_path); + + lib_path = talloc_asprintf(ctx, "%s/" BINDIR_TO_LIBFIPSDIR "/%s", + bin_path, library); + + if (exists (lib_path)) + return lib_path; + + fprintf (stderr, "Error: Failed to find library %s.\n", library); + fprintf (stderr, "Looked in both:\n" + "\t%s\n" + "and\n" + "\t%s/" BINDIR_TO_LIBFIPSDIR "\n", bin_path, bin_path); + exit (1); +} + +/* After forking, set LD_PRELOAD to preload "library" within child + * environment, then exec given arguments. + * + * The "library" argument is the filename (without path) of a shared + * library to load. The complete path will be resolved with + * resolve_library_path above. */ +static int +fork_exec_with_preload_and_wait (char * const argv[], const char *library) { pid_t pid; int i, status; @@ -36,6 +179,15 @@ fork_exec_and_wait (char * const argv[]) /* Child */ if (pid == 0) { + void *ctx = talloc_new (NULL); + char *lib_path; + + lib_path = resolve_path (ctx, library); + + setenv ("LD_PRELOAD", lib_path, 1); + + talloc_free (ctx); + execvp (argv[0], argv); fprintf (stderr, "Failed to execute:"); for (i = 0; argv[i]; i++) { @@ -58,7 +210,7 @@ fork_exec_and_wait (char * const argv[]) } int -execute (int argc, char * const argv[]) +execute_with_preload (int argc, char * const argv[], const char *library) { char **execvp_args; int i; @@ -76,5 +228,5 @@ execute (int argc, char * const argv[]) /* execvp needs final NULL */ execvp_args[i] = NULL; - return fork_exec_and_wait (execvp_args); + return fork_exec_with_preload_and_wait (execvp_args, library); }