From: Michael Tremer Date: Thu, 2 Jan 2025 15:43:01 +0000 (+0000) Subject: ELF: Move RUNPATH check X-Git-Tag: 0.9.30~593 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=60758610fb071e56ef929a06027bf31f7ea89658;p=pakfire.git ELF: Move RUNPATH check Signed-off-by: Michael Tremer --- diff --git a/src/libpakfire/elf.c b/src/libpakfire/elf.c index c5f70e966..1a495c5ec 100644 --- a/src/libpakfire/elf.c +++ b/src/libpakfire/elf.c @@ -697,6 +697,64 @@ int pakfire_elf_has_cf_protection(struct pakfire_elf* self) { return pakfire_elf_foreach_section(self, SHT_NOTE, pakfire_elf_check_cf_protection); } +static int pakfire_elf_check_runpath(struct pakfire_elf* self, + const GElf_Shdr* shdr, const GElf_Dyn* dyn, void* data) { + const char* runpath = NULL; + const char* value = NULL; + char buffer[PATH_MAX]; + char* p = NULL; + int r; + + // The result + char*** runpaths = data; + + switch (dyn->d_tag) { + case DT_RUNPATH: + case DT_RPATH: + // Fetch the value + value = elf_strptr(self->elf, shdr->sh_link, dyn->d_un.d_val); + if (!value) + return 1; + + DEBUG(self->ctx, "%s has a RUNPATH: %s\n", self->path, value); + + // Copy the value into a buffer we can modify + r = pakfire_string_set(buffer, value); + if (r < 0) + goto ERROR; + + // Split the value by : + runpath = strtok_r(buffer, ":", &p); + + // Iterate over all elements + while (runpath) { + r = pakfire_strings_append(runpaths, runpath); + if (r < 0) + goto ERROR; + + // Move on to the next RUNPATH + runpath = strtok_r(NULL, ":", &p); + } + + default: + break; + } + + return 0; + +ERROR: + if (*runpaths) { + pakfire_strings_free(*runpaths); + *runpaths = NULL; + } + + return r; +} + +int pakfire_elf_has_runpaths(struct pakfire_elf* self, char*** runpaths) { + return pakfire_elf_dyn_walk(self, pakfire_elf_check_runpath, runpaths); +} + int pakfire_elf_is_stripped(struct pakfire_elf* self) { Elf_Scn* symtab = NULL; diff --git a/src/libpakfire/include/pakfire/elf.h b/src/libpakfire/include/pakfire/elf.h index 9f6261e21..02de846d1 100644 --- a/src/libpakfire/include/pakfire/elf.h +++ b/src/libpakfire/include/pakfire/elf.h @@ -50,6 +50,7 @@ int pakfire_elf_has_execstack(struct pakfire_elf* self); int pakfire_elf_is_fully_relro(struct pakfire_elf* self); int pakfire_elf_is_partially_relro(struct pakfire_elf* self); int pakfire_elf_has_cf_protection(struct pakfire_elf* self); +int pakfire_elf_has_runpaths(struct pakfire_elf* self, char*** runpaths); int pakfire_elf_is_stripped(struct pakfire_elf* self); // Return bitmap for pakfire_elf_has_cf_protection() diff --git a/src/libpakfire/include/pakfire/string.h b/src/libpakfire/include/pakfire/string.h index e3974e90a..809bb30ec 100644 --- a/src/libpakfire/include/pakfire/string.h +++ b/src/libpakfire/include/pakfire/string.h @@ -80,6 +80,20 @@ static inline int pakfire_string_contains_whitespace(const char* s) { Cleanup Stuff */ +inline size_t pakfire_strings_length(char** array) { + size_t length = 0; + + // If we have received NULL, we will return zero + if (!array) + return 0; + + // Count all elements + for (char** s = array; *s; s++) + length++; + + return length; +} + inline void pakfire_strings_free(char** array) { for (char** s = array; *s; s++) free(*s); @@ -87,6 +101,30 @@ inline void pakfire_strings_free(char** array) { free(array); } +inline int pakfire_strings_append(char*** array, const char* s) { + if (!array) + return -EINVAL; + + // Fetch the length + size_t length = pakfire_strings_length(*array); + + // Resize the array + *array = reallocarray(*array, length + 2, sizeof(**array)); + if (!*array) + return -errno; + + // Copy the string + char* p = *array[length++] = strdup(s); + if (!p) + return -errno; + + // Terminate the array + *array[length] = NULL; + + // Return the total length of the array + return length; +} + #define TIME_STRING_MAX 32 #define pakfire_format_size(dst, value) \ diff --git a/src/libpakfire/linter-file.c b/src/libpakfire/linter-file.c index f8ff6a501..340823080 100644 --- a/src/libpakfire/linter-file.c +++ b/src/libpakfire/linter-file.c @@ -346,71 +346,6 @@ static int pakfire_linter_check_script_interpreter(struct pakfire_linter_file* l return 0; } -static int pakfire_linter_file_get_elf_section(struct pakfire_linter_file* lfile, - const Elf64_Word type, Elf_Scn** section, GElf_Shdr* header, Elf_Data** data) { - Elf_Scn* s = NULL; - GElf_Shdr shdr; - - // Walk through all sections - for (;;) { - s = elf_nextscn(lfile->elf, s); - if (!s) - break; - - // Fetch the section header - gelf_getshdr(s, &shdr); - - // Return any matching sections - if (shdr.sh_type == type) { - *section = s; - - // Send header if requested - if (header) - gelf_getshdr(s, header); - - // Send data if requested - if (data) - *data = elf_getdata(s, NULL); - - return 0; - } - } - - // No section found - return 1; -} - -static int pakfire_linter_file_elf_dyn_walk(struct pakfire_linter_file* lfile, - int (*callback)(struct pakfire_linter_file* lfile, - const GElf_Shdr* shdr, const GElf_Dyn* dyn, void* data), void* data) { - Elf_Scn* dynamic = NULL; - GElf_Shdr shdr; - Elf_Data* elf_data = NULL; - GElf_Dyn dyn; - int r; - - // Find the dynamic linking information - r = pakfire_linter_file_get_elf_section(lfile, SHT_DYNAMIC, &dynamic, &shdr, &elf_data); - if (r) { - DEBUG(lfile->ctx, "%s does not have a dynamic section\n", lfile->path); - return 0; - } - - // Walk through all entries... - for (unsigned int i = 0; ; i++) { - // Fetch the next entry - if (!gelf_getdyn(elf_data, i, &dyn)) - break; - - // Call the callback - r = callback(lfile, &shdr, &dyn, data); - if (r) - return r; - } - - return 0; -} - static int pakfire_linter_file_check_pie(struct pakfire_linter_file* lfile) { if (!pakfire_elf_is_pie(lfile->_elf)) return pakfire_linter_file_error(lfile, "Missing PIE"); @@ -465,74 +400,40 @@ static int pakfire_linter_file_check_relro(struct pakfire_linter_file* lfile) { return pakfire_linter_file_error(lfile, "Is not RELRO"); } -/* - RPATH/RUNPATH -*/ -static int __pakfire_linter_file_process_runpath(struct pakfire_linter_file* lfile, - const GElf_Shdr* shdr, const GElf_Dyn* dyn, void* data) { - const char* value = NULL; - const char* runpath = NULL; - char buffer[PATH_MAX]; - char* p = NULL; +static int pakfire_linter_file_check_runpath(struct pakfire_linter_file* lfile) { + char** runpaths = NULL; int r; - switch (dyn->d_tag) { - case DT_RUNPATH: - case DT_RPATH: - // Fetch the value - value = elf_strptr(lfile->elf, shdr->sh_link, dyn->d_un.d_val); - if (!value) - return 1; - - DEBUG(lfile->ctx, "%s has a RUNPATH: %s\n", lfile->path, value); - - // Copy the value into a buffer we can modify - r = pakfire_string_set(buffer, value); - if (r < 0) - return r; - - // Split the value by : - runpath = strtok_r(buffer, ":", &p); - - // Iterate over all elements - while (runpath) { - DEBUG(lfile->ctx, "Checking RUNPATH %s\n", runpath); - - // We do not allow any relative RUNPATHs - if (pakfire_path_match(runpath, "**/../**")) - goto RUNPATH_DENIED; - - // We allow /usr/lib64 as libtool seems to link it in quite a lot - if (pakfire_path_match("/usr/lib64", runpath)) - goto RUNPATH_PERMITTED; + // Fetch any runpaths + r = pakfire_elf_has_runpaths(lfile->_elf, &runpaths); + if (r < 0) + goto ERROR; - // We allow any subdirectories of /usr/lib64 - if (pakfire_path_match( "/usr/lib64/**", runpath)) - goto RUNPATH_PERMITTED; + // Report any invalid runpaths + if (runpaths) { + for (char** runpath = runpaths; *runpath; runpath++) { + DEBUG(lfile->ctx, "Checking RUNPATH %s\n", *runpath); -RUNPATH_DENIED: - // If we make it here, this check has failed - r = pakfire_linter_file_error(lfile, "Has illegal RPATH/RUNPATH %s", runpath); - if (r < 0) - return r; + // We allow /usr/lib64 as libtool seems to link it in quite a lot + if (pakfire_path_match("/usr/lib64", *runpath)) + continue; - break; + // We allow any subdirectories of /usr/lib64 + if (pakfire_path_match( "/usr/lib64/**", *runpath)) + continue; -RUNPATH_PERMITTED: - // Move on to the next RUNPATH - runpath = strtok_r(NULL, ":", &p); - } - - default: - break; + // This seems to be an illegal runpath + r = pakfire_linter_file_error(lfile, "Has illegal RPATH/RUNPATH %s", *runpath); + if (r < 0) + goto ERROR; + } } - return 0; -} +ERROR: + if (runpaths) + pakfire_strings_free(runpaths); -static int pakfire_linter_file_check_runpath(struct pakfire_linter_file* lfile) { - return pakfire_linter_file_elf_dyn_walk( - lfile, __pakfire_linter_file_process_runpath, NULL); + return r; } static int pakfire_linter_file_check_cf_protection(struct pakfire_linter_file* lfile) {