From 840579bfa811914e853c7b5ad2f0a71d99e9496d Mon Sep 17 00:00:00 2001 From: Michael Tremer Date: Sat, 26 Oct 2024 18:12:39 +0000 Subject: [PATCH] linter: Parse the ELF file early to avoid this callback hell Signed-off-by: Michael Tremer --- src/libpakfire/linter-file.c | 319 +++++++++++++---------------------- src/libpakfire/linter.c | 4 +- 2 files changed, 120 insertions(+), 203 deletions(-) diff --git a/src/libpakfire/linter-file.c b/src/libpakfire/linter-file.c index 6dd8dfef5..ee39d6143 100644 --- a/src/libpakfire/linter-file.c +++ b/src/libpakfire/linter-file.c @@ -54,6 +54,9 @@ struct pakfire_linter_file { // Path const char* path; + + // ELF + Elf* elf; }; #define pakfire_linter_file_info(lfile, format, ...) \ @@ -63,6 +66,32 @@ struct pakfire_linter_file { #define pakfire_linter_file_error(lfile, format, ...) \ pakfire_linter_result(lfile->linter, lfile->file, PAKFIRE_LINTER_ERROR, format, ## __VA_ARGS__) +static int pakfire_linter_file_init_libelf(struct pakfire_linter_file* lfile) { + // Initialize libelf + if (elf_version(EV_CURRENT) == EV_NONE) { + ERROR(lfile->ctx, "Could not initialize libelf: %s\n", elf_errmsg(-1)); + return -EINVAL; + } + + return 0; +} + +static int pakfire_linter_file_open_elf(struct pakfire_linter_file* lfile) { + int r; + + // Initialize libelf + r = pakfire_linter_file_init_libelf(lfile); + if (r < 0) + return r; + + // Parse the ELF header + lfile->elf = elf_memory(lfile->data, lfile->length); + if (!lfile->elf) + return -errno; + + return 0; +} + /* Maps the file into memory */ @@ -128,6 +157,13 @@ int pakfire_linter_file_create(struct pakfire_linter_file** lfile, goto ERROR; } + // Initialize libelf + r = pakfire_linter_file_open_elf(l); + if (r < 0) { + ERROR(l->ctx, "Could not initialize libelf: %s\n", strerror(-r)); + goto ERROR; + } + // Return the pointer *lfile = pakfire_linter_file_ref(l); @@ -141,20 +177,25 @@ ERROR: static void pakfire_linter_file_free(struct pakfire_linter_file* lfile) { int r; + if (lfile->elf) + elf_end(lfile->elf); + if (lfile->data) { r = munmap(lfile->data, lfile->length); if (r < 0) ERROR(lfile->ctx, "Could not unmmap %s: %m\n", lfile->path); } + if (lfile->fd >= 0) + close(lfile->fd); + if (lfile->linter) pakfire_linter_unref(lfile->linter); if (lfile->file) pakfire_file_unref(lfile->file); if (lfile->ctx) pakfire_ctx_unref(lfile->ctx); - if (lfile->fd >= 0) - close(lfile->fd); + free(lfile); } struct pakfire_linter_file* pakfire_linter_file_ref(struct pakfire_linter_file* lfile) { @@ -284,128 +325,45 @@ static int pakfire_linter_check_script_interpreter(struct pakfire_linter_file* l return 0; } -static int pakfire_linter_file_init_libelf(struct pakfire_linter_file* lfile) { - // Initialize libelf - if (elf_version(EV_CURRENT) == EV_NONE) { - ERROR(lfile->ctx, "Could not initialize libelf: %s\n", elf_errmsg(-1)); - return -EINVAL; - } - - return 0; -} static int pakfire_linter_file_is_elf(struct pakfire_linter_file* lfile) { - Elf* elf = NULL; - int r; - - // Initialize libelf - r = pakfire_linter_file_init_libelf(lfile); - if (r < 0) - return r; - - // Parse the ELF header - elf = elf_begin(lfile->fd, ELF_C_READ, NULL); - if (!elf) { - ERROR(lfile->ctx, "Could not open ELF file: %s\n", elf_errmsg(-1)); - r = -EINVAL; - goto ERROR; - } + // Don't know if we don't have an ELF object + if (!lfile->elf) + return -EINVAL; - switch (elf_kind(elf)) { + switch (elf_kind(lfile->elf)) { case ELF_K_ELF: - r = 1; - break; + return 1; // Ignore everything else default: - r = 0; break; } -ERROR: - if (elf) - elf_end(elf); - - return r; -} - -/* - A helper function that opens an ELF file and calls a callback. -*/ -static int pakfire_linter_file_elf(struct pakfire_linter_file* lfile, - int (*callback)(struct pakfire_linter_file* lfile, Elf* elf, void* data), void* data) { - Elf* elf = NULL; - int r; - - // Initialize libelf - r = pakfire_linter_file_init_libelf(lfile); - if (r < 0) - return r; - - // Parse the ELF header - elf = elf_begin(lfile->fd, ELF_C_READ, NULL); - if (!elf) { - ERROR(lfile->ctx, "Could not open ELF file: %s\n", elf_errmsg(-1)); - r = -EINVAL; - goto ERROR; - } - - // Check if this is an ELF file - switch (elf_kind(elf)) { - case ELF_K_ELF: - break; - - default: - ERROR(lfile->ctx, "%s is not an ELF object\n", lfile->path); - r = -EINVAL; - goto ERROR; - } - - // Call the callback - r = callback(lfile, elf, data); - -ERROR: - if (elf) - elf_end(elf); - - return r; + return 0; } -static int __pakfire_linter_file_get_elf_type( - struct pakfire_linter_file* lfile, Elf* elf, void* data) { +static int pakfire_linter_file_get_elf_type(struct pakfire_linter_file* lfile) { GElf_Ehdr ehdr = {}; - int* type = data; + // Check if have an ELF object + if (lfile->elf) + return -EINVAL; // Fetch the ELF header - if (!gelf_getehdr(elf, &ehdr)) { + if (!gelf_getehdr(lfile->elf, &ehdr)) { ERROR(lfile->ctx, "Could not parse ELF header: %s\n", elf_errmsg(-1)); return -EINVAL; } - // Store the type - *type = ehdr.e_type; - - return 0; -} - -static int pakfire_linter_file_get_elf_type(struct pakfire_linter_file* lfile) { - int type = ET_NONE; - int r; - - // Fetch the type - r = pakfire_linter_file_elf(lfile, __pakfire_linter_file_get_elf_type, &type); - if (r < 0) - return r; - - return type; + return ehdr.e_type; } typedef int (*__pakfire_linter_file_for_elf_section_callback)(struct pakfire_linter_file* lfile, - Elf* elf, const Elf_Scn* section, const GElf_Shdr* shdr, Elf_Data* data); + const Elf_Scn* section, const GElf_Shdr* shdr, Elf_Data* data); static int pakfire_linter_file_for_elf_section(struct pakfire_linter_file* lfile, - Elf* elf, const Elf64_Word type, __pakfire_linter_file_for_elf_section_callback callback) { + const Elf64_Word type, __pakfire_linter_file_for_elf_section_callback callback) { Elf_Scn* section = NULL; GElf_Shdr shdr; Elf_Data* data; @@ -413,7 +371,7 @@ static int pakfire_linter_file_for_elf_section(struct pakfire_linter_file* lfile // Walk through all sections for (;;) { - section = elf_nextscn(elf, section); + section = elf_nextscn(lfile->elf, section); if (!section) break; @@ -428,7 +386,7 @@ static int pakfire_linter_file_for_elf_section(struct pakfire_linter_file* lfile data = elf_getdata(section, NULL); // Call the callback - r = callback(lfile, elf, section, &shdr, data); + r = callback(lfile, section, &shdr, data); if (r) break; } @@ -438,14 +396,13 @@ static int pakfire_linter_file_for_elf_section(struct pakfire_linter_file* lfile static int pakfire_linter_file_get_elf_section(struct pakfire_linter_file* lfile, - Elf* elf, const Elf64_Word type, Elf_Scn** section, GElf_Shdr* header, Elf_Data** data) { + 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(elf, s); + s = elf_nextscn(lfile->elf, s); if (!s) break; @@ -472,8 +429,8 @@ static int pakfire_linter_file_get_elf_section(struct pakfire_linter_file* lfile return 1; } -static int pakfire_linter_file_elf_dyn_walk(struct pakfire_linter_file* lfile, Elf* elf, - int (*callback)(struct pakfire_linter_file* lfile, Elf* elf, +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; @@ -482,7 +439,7 @@ static int pakfire_linter_file_elf_dyn_walk(struct pakfire_linter_file* lfile, E int r; // Find the dynamic linking information - r = pakfire_linter_file_get_elf_section(lfile, elf, SHT_DYNAMIC, &dynamic, &shdr, &elf_data); + 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; @@ -495,7 +452,7 @@ static int pakfire_linter_file_elf_dyn_walk(struct pakfire_linter_file* lfile, E break; // Call the callback - r = callback(lfile, elf, &shdr, &dyn, data); + r = callback(lfile, &shdr, &dyn, data); if (r) return r; } @@ -503,7 +460,6 @@ static int pakfire_linter_file_elf_dyn_walk(struct pakfire_linter_file* lfile, E return 0; } - static int pakfire_linter_file_check_pie(struct pakfire_linter_file* lfile) { switch (pakfire_linter_file_get_elf_type(lfile)) { // Shared Object files are good @@ -518,8 +474,7 @@ static int pakfire_linter_file_check_pie(struct pakfire_linter_file* lfile) { return pakfire_linter_file_error(lfile, "Missing PIE"); } -static int __pakfire_linter_file_check_ssp( - struct pakfire_linter_file* lfile, Elf* elf, void* data) { +static int pakfire_linter_file_check_ssp(struct pakfire_linter_file* lfile) { Elf_Scn* symtab = NULL; GElf_Shdr shdr; Elf_Data* elf_data = NULL; @@ -527,8 +482,27 @@ static int __pakfire_linter_file_check_ssp( const char* name = NULL; int r; + // This check will be skipped for these files + static const char* whitelist[] = { + "/usr/lib64/libgcc_s.so.*", + "/usr/lib64/libmvec.so.*", + NULL, + }; + + // Do not perform this check for runtime linkers + if (pakfire_file_matches(lfile->file, "/usr/lib*/ld-*.so*")) + return 0; + + // Check if this file is whitelisted + for (const char** path = whitelist; *path; path++) { + if (pakfire_file_matches(lfile->file, *path)) { + DEBUG(lfile->ctx, "Skipping SSP check for whitelisted file %s\n", lfile->path); + return 0; + } + } + // Fetch the symbol table - r = pakfire_linter_file_get_elf_section(lfile, elf, SHT_SYMTAB, &symtab, &shdr, &elf_data); + r = pakfire_linter_file_get_elf_section(lfile, SHT_SYMTAB, &symtab, &shdr, &elf_data); if (r) { ERROR(lfile->ctx, "%s has no symbol table\n", lfile->path); return 1; @@ -542,7 +516,7 @@ static int __pakfire_linter_file_check_ssp( gelf_getsym(elf_data, i, &symbol); // Fetch the symbol name - name = elf_strptr(elf, shdr.sh_link, symbol.st_name); + name = elf_strptr(lfile->elf, shdr.sh_link, symbol.st_name); // Skip empty section names if (!name || !*name) @@ -577,38 +551,14 @@ static int __pakfire_linter_file_check_ssp( return pakfire_linter_file_error(lfile, "Missing Stack Smashing Protection"); } -static int pakfire_linter_file_check_ssp(struct pakfire_linter_file* lfile) { - // This check will be skipped for these files - static const char* whitelist[] = { - "/usr/lib64/libgcc_s.so.*", - "/usr/lib64/libmvec.so.*", - NULL, - }; - - // Do not perform this check for runtime linkers - if (pakfire_file_matches(lfile->file, "/usr/lib*/ld-*.so*")) - return 0; - - // Check if this file is whitelisted - for (const char** path = whitelist; *path; path++) { - if (pakfire_file_matches(lfile->file, *path)) { - DEBUG(lfile->ctx, "Skipping SSP check for whitelisted file %s\n", lfile->path); - return 0; - } - } - - return pakfire_linter_file_elf(lfile, __pakfire_linter_file_check_ssp, NULL); -} - -static int __pakfire_linter_file_check_execstack( - struct pakfire_linter_file* lfile, Elf* elf, void* data) { +static int pakfire_linter_file_check_execstack(struct pakfire_linter_file* lfile) { GElf_Phdr phdr; int r; size_t phnum = 0; // Fetch the total numbers of program headers - r = elf_getphdrnum(elf, &phnum); + r = elf_getphdrnum(lfile->elf, &phnum); if (r) { ERROR(lfile->ctx, "Could not fetch number of program headers: %s\n", elf_errmsg(-1)); @@ -617,7 +567,7 @@ static int __pakfire_linter_file_check_execstack( // Walk through all program headers for (unsigned int i = 0; i < phnum; i++) { - if (!gelf_getphdr(elf, i, &phdr)) { + if (!gelf_getphdr(lfile->elf, i, &phdr)) { ERROR(lfile->ctx, "Could not parse program header: %s\n", elf_errmsg(-1)); return -ENOTSUP; } @@ -644,12 +594,8 @@ static int __pakfire_linter_file_check_execstack( return 0; } -static int pakfire_linter_file_check_execstack(struct pakfire_linter_file* lfile) { - return pakfire_linter_file_elf(lfile, __pakfire_linter_file_check_execstack, NULL); -} - static int __pakfire_linter_file_has_bind_now(struct pakfire_linter_file* lfile, - Elf* elf, const GElf_Shdr* shdr, const GElf_Dyn* dyn, void* data) { + const GElf_Shdr* shdr, const GElf_Dyn* dyn, void* data) { int* has_bind_now = (int*)data; switch (dyn->d_tag) { @@ -674,14 +620,13 @@ static int __pakfire_linter_file_has_bind_now(struct pakfire_linter_file* lfile, return 0; } -static int __pakfire_linter_file_check_relro( - struct pakfire_linter_file* lfile, Elf* elf, void* data) { +static int pakfire_linter_file_check_relro(struct pakfire_linter_file* lfile) { int has_bind_now = 0; GElf_Phdr phdr; int r; // Check if we have BIND_NOW - r = pakfire_linter_file_elf_dyn_walk(lfile, elf, + r = pakfire_linter_file_elf_dyn_walk(lfile, __pakfire_linter_file_has_bind_now, &has_bind_now); if (r) return r; @@ -692,7 +637,7 @@ static int __pakfire_linter_file_check_relro( // Walk through all program headers for (unsigned int i = 0;; i++) { - if (!gelf_getphdr(elf, i, &phdr)) + if (!gelf_getphdr(lfile->elf, i, &phdr)) break; switch (phdr.p_type) { @@ -711,15 +656,11 @@ static int __pakfire_linter_file_check_relro( return pakfire_linter_file_error(lfile, "Is not fully RELRO"); } -static int pakfire_linter_file_check_relro(struct pakfire_linter_file* lfile) { - return pakfire_linter_file_elf(lfile, __pakfire_linter_file_check_relro, NULL); -} - /* RPATH/RUNPATH */ static int __pakfire_linter_file_process_runpath(struct pakfire_linter_file* lfile, - Elf* elf, const GElf_Shdr* shdr, const GElf_Dyn* dyn, void* data) { + const GElf_Shdr* shdr, const GElf_Dyn* dyn, void* data) { const char* value = NULL; const char* runpath = NULL; char buffer[PATH_MAX]; @@ -730,7 +671,7 @@ static int __pakfire_linter_file_process_runpath(struct pakfire_linter_file* lfi case DT_RUNPATH: case DT_RPATH: // Fetch the value - value = elf_strptr(elf, shdr->sh_link, dyn->d_un.d_val); + value = elf_strptr(lfile->elf, shdr->sh_link, dyn->d_un.d_val); if (!value) return 1; @@ -780,14 +721,9 @@ RUNPATH_PERMITTED: return 0; } -static int __pakfire_linter_file_check_runpath( - struct pakfire_linter_file* lfile, Elf* elf, void* data) { - return pakfire_linter_file_elf_dyn_walk( - lfile, elf, __pakfire_linter_file_process_runpath, data); -} - static int pakfire_linter_file_check_runpath(struct pakfire_linter_file* lfile) { - return pakfire_linter_file_elf(lfile, __pakfire_linter_file_check_runpath, NULL); + return pakfire_linter_file_elf_dyn_walk( + lfile, __pakfire_linter_file_process_runpath, NULL); } static uint32_t read_4_bytes(const int endianess, const char* data) { @@ -850,8 +786,7 @@ static int __pakfire_linter_file_check_cf_protection_x86(struct pakfire_linter_f return 0; } -static int pakfire_linter_file_check_cf_protection_callback( - struct pakfire_linter_file* lfile, Elf* elf, +static int pakfire_linter_file_check_cf_protection_callback(struct pakfire_linter_file* lfile, const Elf_Scn* section, const GElf_Shdr* shdr, Elf_Data* data) { GElf_Ehdr ehdr = {}; GElf_Nhdr nhdr = {}; @@ -861,7 +796,7 @@ static int pakfire_linter_file_check_cf_protection_callback( int r; // Fetch the ELF header - if (!gelf_getehdr(elf, &ehdr)) { + if (!gelf_getehdr(lfile->elf, &ehdr)) { ERROR(lfile->ctx, "Could not fetch the ELF header for %s: %m\n", lfile->path); return -errno; } @@ -948,34 +883,15 @@ static int pakfire_linter_file_check_cf_protection_callback( return 0; } -static int __pakfire_linter_file_check_cf_protection( - struct pakfire_linter_file* lfile, Elf* elf, void* data) { - return pakfire_linter_file_for_elf_section( - lfile, elf, SHT_NOTE, pakfire_linter_file_check_cf_protection_callback); -} - static int pakfire_linter_file_check_cf_protection(struct pakfire_linter_file* lfile) { - return pakfire_linter_file_elf(lfile, __pakfire_linter_file_check_cf_protection, NULL); + return pakfire_linter_file_for_elf_section( + lfile, SHT_NOTE, pakfire_linter_file_check_cf_protection_callback); } -static int __pakfire_linter_file_is_stripped( - struct pakfire_linter_file* lfile, Elf* elf, void* data) { +static int pakfire_linter_file_is_stripped(struct pakfire_linter_file* lfile) { Elf_Scn* symtab = NULL; int r; - // Fetch the symbol table - r = pakfire_linter_file_get_elf_section(lfile, elf, SHT_SYMTAB, &symtab, NULL, NULL); - if (r < 0) - return r; - - // If we have found the symbol table we are not stripped - else if (r == 0) - return pakfire_linter_file_error(lfile, "Not Stripped"); - - return 0; -} - -static int pakfire_linter_file_is_stripped(struct pakfire_linter_file* lfile) { switch (pakfire_linter_file_get_elf_type(lfile)) { // Do not check Relocatable Objects case ET_REL: @@ -986,16 +902,24 @@ static int pakfire_linter_file_is_stripped(struct pakfire_linter_file* lfile) { break; } - return pakfire_linter_file_elf(lfile, __pakfire_linter_file_is_stripped, NULL); + // Fetch the symbol table + r = pakfire_linter_file_get_elf_section(lfile, SHT_SYMTAB, &symtab, NULL, NULL); + if (r < 0) + return r; + + // If we have found the symbol table we are not stripped + else if (r == 0) + return pakfire_linter_file_error(lfile, "Not Stripped"); + + return 0; } -static int __pakfire_linter_file_has_debuglink( - struct pakfire_linter_file* lfile, Elf* elf, void* data) { +static int pakfire_linter_file_has_debuglink(struct pakfire_linter_file* lfile) { const char* name = NULL; GElf_Word crc32; // Fetch the debug link - name = dwelf_elf_gnu_debuglink(elf, &crc32); + name = dwelf_elf_gnu_debuglink(lfile->elf, &crc32); if (!name) return pakfire_linter_file_error(lfile, "Missing Debug Link"); @@ -1004,17 +928,12 @@ static int __pakfire_linter_file_has_debuglink( return 0; } -static int pakfire_linter_file_has_debuglink(struct pakfire_linter_file* lfile) { - return pakfire_linter_file_elf(lfile, __pakfire_linter_file_has_debuglink, NULL); -} - -static int __pakfire_linter_file_has_buildid( - struct pakfire_linter_file* lfile, Elf* elf, void* data) { +static int pakfire_linter_file_has_buildid(struct pakfire_linter_file* lfile) { const void* buildid = NULL; ssize_t length = 0; // Fetch the build ID - length = dwelf_elf_gnu_build_id(elf, &buildid); + length = dwelf_elf_gnu_build_id(lfile->elf, &buildid); if (length < 0) return length; @@ -1025,10 +944,6 @@ static int __pakfire_linter_file_has_buildid( return 0; } -static int pakfire_linter_file_has_buildid(struct pakfire_linter_file* lfile) { - return pakfire_linter_file_elf(lfile, __pakfire_linter_file_has_buildid, NULL); -} - int pakfire_linter_file_lint(struct pakfire_linter_file* lfile) { int r = 0; diff --git a/src/libpakfire/linter.c b/src/libpakfire/linter.c index d0deeec6f..5bc14ac4b 100644 --- a/src/libpakfire/linter.c +++ b/src/libpakfire/linter.c @@ -371,8 +371,10 @@ static int pakfire_linter_payload( // Lint the file r = pakfire_linter_file_lint(lfile); - if (r < 0) + if (r < 0) { + ERROR(linter->ctx, "Could not lint %s: %s\n", path, strerror(-r)); goto ERROR; + } ERROR: if (lfile) -- 2.39.5