diff options
-rw-r--r-- | elf/dl-dst.h | 9 | ||||
-rw-r--r-- | elf/dl-load.c | 10 | ||||
-rw-r--r-- | elf/dl-support.c | 3 | ||||
-rw-r--r-- | elf/rtld.c | 88 | ||||
-rw-r--r-- | sysdeps/generic/ldsodefs.h | 3 |
5 files changed, 110 insertions, 3 deletions
diff --git a/elf/dl-dst.h b/elf/dl-dst.h index 32de5d2..f00c8e2 100644 --- a/elf/dl-dst.h +++ b/elf/dl-dst.h @@ -65,8 +65,13 @@ else \ dst_len = (l)->l_origin == (char *) -1 \ ? 0 : strlen ((l)->l_origin); \ - dst_len = MAX (MAX (dst_len, GLRO(dl_platformlen)), \ - strlen (DL_DST_LIB)); \ + \ + char *exec_origin = GLRO(google_exec_origin_dir); \ + size_t exec_origin_len = \ + (exec_origin == NULL) ? 0 : strlen (exec_origin); \ + \ + dst_len = MAX (MAX (MAX (dst_len, GLRO(dl_platformlen)), \ + strlen (DL_DST_LIB)), exec_origin_len); \ if (dst_len > 4) \ __len += __cnt * (dst_len - 4); \ } \ diff --git a/elf/dl-load.c b/elf/dl-load.c index 7554a99..50cbed2 100644 --- a/elf/dl-load.c +++ b/elf/dl-load.c @@ -232,7 +232,8 @@ _dl_dst_count (const char *name) ++name; if ((len = is_dst (start, name, "ORIGIN", __libc_enable_secure)) != 0 || (len = is_dst (start, name, "PLATFORM", 0)) != 0 - || (len = is_dst (start, name, "LIB", 0)) != 0) + || (len = is_dst (start, name, "LIB", 0)) != 0 + || (len = is_dst (start, name, "EXEC_ORIGIN", 0)) != 0) ++cnt; name = strchr (name + len, '$'); @@ -274,6 +275,13 @@ _dl_dst_substitute (struct link_map *l, const char *name, char *result) repl = GLRO(dl_platform); else if ((len = is_dst (start, name, "LIB", 0)) != 0) repl = DL_DST_LIB; + else if ((len = is_dst (start, name, "EXEC_ORIGIN", is_path, 0)) != 0) + { + if (INTUSE(__libc_enable_secure) != 0) + _dl_fatal_printf ("$EXEC_ORIGIN rpath entry not allowed in setuid/setgid executables.\n"); + + repl = GLRO(google_exec_origin_dir); + } if (repl != NULL && repl != (const char *) -1) { diff --git a/elf/dl-support.c b/elf/dl-support.c index 114f77a..febc5e2 100644 --- a/elf/dl-support.c +++ b/elf/dl-support.c @@ -67,6 +67,9 @@ void *__libc_stack_end; /* Path where the binary is found. */ const char *_dl_origin_path; +/* Directory where the AT_EXECFN is found. */ +const char *_google_exec_origin_dir; + /* Nonzero if runtime lookup should not update the .got/.plt. */ int _dl_bind_not; @@ -51,6 +51,12 @@ extern __typeof (__mempcpy) __mempcpy attribute_hidden; extern __typeof (_exit) exit_internal asm ("_exit") attribute_hidden; #define _exit exit_internal +/* Iterate over auxv, find AT_EXECFN if any. */ +static char * get_at_execfn(ElfW(auxv_t) *auxv); + +/* Given file path, return fully resolved directory path. */ +static char * get_directory (const char *file_path); + /* Helper function to handle errors while resolving symbols. */ static void print_unresolved (int errcode, const char *objname, const char *errsting); @@ -73,6 +79,9 @@ enum mode { normal, list, verify, trace }; all the entries. */ static void process_envvars (enum mode *modep); +/* Set GLRO(google_exec_origin_dir). */ +static void set_exec_origin_dir(const char *exe_path); + #ifdef DL_ARGV_NOT_RELRO int _dl_argc attribute_hidden; char **_dl_argv = NULL; @@ -1032,6 +1041,8 @@ of this helper program; chances are you did not intend to run this program.\n\ in LIST\n\ --audit LIST use objects named in LIST as auditors\n"); + set_exec_origin_dir (INTUSE(_dl_argv)[1]); + ++_dl_skip_args; --_dl_argc; ++_dl_argv; @@ -1126,6 +1137,8 @@ of this helper program; chances are you did not intend to run this program.\n\ } else { + set_exec_origin_dir (get_at_execfn (auxv)); + /* Create a link_map for the executable itself. This will be what dlopen on "" returns. */ main_map = _dl_new_object ((char *) "", "", lt_executable, NULL, @@ -2811,6 +2824,81 @@ print_statistics (hp_timing_t *rtld_total_timep) #endif } +/* Given file path, return an absolute directory path. + Examples: in: "/foo/bar/a.out", out: "/foo/bar/"; + in: "./a.out", out: "/dot/resolved/to/full/path/./". */ +static char * +get_directory (const char *file_path) +{ + assert (file_path != NULL); + + /* Find the end of the directory substring in file_path. */ + size_t path_len = strlen (file_path); + while (path_len > 0 && file_path[path_len - 1] != '/') + --path_len; + + /* Allocate space and set the path prefix according to whether or not + this is an absolute path. */ + char *dest; + char *full_dir_path; + if (file_path[0] == '/') + { + full_dir_path = malloc (path_len + 1); + assert (full_dir_path != NULL); + dest = full_dir_path; + } + else + { + /* For a relative path, we need to include space for the largest + possible current path, a joining '/', the relevant part of + file_path, and a trailing '\0'. */ + full_dir_path = malloc (PATH_MAX + path_len + 2); + assert (full_dir_path != NULL); + + char *status = __getcwd (full_dir_path, PATH_MAX); + assert (status != NULL); + + dest = __rawmemchr (full_dir_path, '\0'); + if (dest[-1] != '/') + *dest++ = '/'; + } + + if (path_len > 0) + dest = __mempcpy (dest, file_path, path_len); + *dest = '\0'; + + /* Confirm that the constructed path is valid. */ + struct stat64 st; + assert (__xstat64 (_STAT_VER, full_dir_path, &st) == 0); + + return full_dir_path; +} + +/* Set GLRO(google_exec_origin_dir). */ +static void +set_exec_origin_dir (const char *exe_path) +{ + assert (GLRO(google_exec_origin_dir) == NULL); + + if (GLRO(dl_origin_path) != NULL) + GLRO(google_exec_origin_dir) = strdup (GLRO(dl_origin_path)); + else if (exe_path != NULL) + GLRO(google_exec_origin_dir) = get_directory (exe_path); +} + +/* Iterate over auxv, find AT_EXECFN if any. */ +static char * +get_at_execfn (ElfW(auxv_t) *auxv) +{ + assert (auxv != NULL); + + for (; auxv->a_type != AT_NULL; ++auxv) + if (auxv->a_type == AT_EXECFN) + return (char *) auxv->a_un.a_val; + + return NULL; +} + #ifndef NESTING char *dummy1 = (char *)elf_get_dynamic_info; # if ! ELF_MACHINE_NO_REL diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h index a9c2b08..caa5f1d 100644 --- a/sysdeps/generic/ldsodefs.h +++ b/sysdeps/generic/ldsodefs.h @@ -544,6 +544,9 @@ struct rtld_global_ro /* Location of the binary. */ EXTERN const char *_dl_origin_path; + /* Directory where the AT_EXECFN is found. */ + EXTERN const char *_google_exec_origin_dir; + /* -1 if the dynamic linker should honor library load bias, 0 if not, -2 use the default (honor biases for normal binaries, don't honor for PIEs). */ |