From: Michael Tremer Date: Sat, 26 Oct 2024 12:02:34 +0000 (+0000) Subject: linter: Move CF protection check X-Git-Tag: 0.9.30~846 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=40482c7d3e13530ce1d471bdea3f4cb617ee4569;p=pakfire.git linter: Move CF protection check Signed-off-by: Michael Tremer --- diff --git a/src/libpakfire/file.c b/src/libpakfire/file.c index faabd1801..23da7dc81 100644 --- a/src/libpakfire/file.c +++ b/src/libpakfire/file.c @@ -1839,45 +1839,6 @@ ERROR: return r; } -typedef int (*__pakfire_file_for_elf_section_callback)( - struct pakfire_file* file, - Elf* elf, - const Elf_Scn* section, - const GElf_Shdr* shdr, - Elf_Data* data); - -static int pakfire_file_for_elf_section(struct pakfire_file* file, - Elf* elf, const Elf64_Word type, __pakfire_file_for_elf_section_callback callback) { - Elf_Scn* section = NULL; - GElf_Shdr shdr; - Elf_Data* data; - int r = -EINVAL; - - // Walk through all sections - for (;;) { - section = elf_nextscn(elf, section); - if (!section) - break; - - // Fetch the section header - gelf_getshdr(section, &shdr); - - // Skip sections that don't match - if (type && shdr.sh_type != type) - continue; - - // Fetch the data - data = elf_getdata(section, NULL); - - // Call the callback - r = callback(file, elf, section, &shdr, data); - if (r) - break; - } - - return r; -} - static int pakfire_file_get_elf_section(struct pakfire_file* file, Elf* elf, const Elf64_Word type, Elf_Scn** section, GElf_Shdr* header, Elf_Data** data) { Elf_Scn* s = NULL; @@ -1940,38 +1901,6 @@ static int pakfire_file_get_elf_type(struct pakfire_file* file) { return type; } -static int pakfire_file_elf_dyn_walk(struct pakfire_file* file, Elf* elf, - int (*callback)(struct pakfire_file* file, - Elf* elf, 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_file_get_elf_section(file, elf, SHT_DYNAMIC, &dynamic, &shdr, &elf_data); - if (r) { - DEBUG(file->ctx, "%s does not have a dynamic section\n", pakfire_file_get_path(file)); - 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(file, elf, &shdr, &dyn, data); - if (r) - return r; - } - - return 0; -} - static int __pakfire_file_check_debuginfo(struct pakfire_file* file, Elf* elf, void* data) { Elf_Scn* symtab = NULL; int r; @@ -2320,169 +2249,6 @@ static int pakfire_file_check_capabilities(struct pakfire_file* file) { return 0; } -static uint32_t read_4_bytes(const int endianess, const char* data) { - uint32_t bytes = 0; - - switch (endianess) { - // Litte Endian - case ELFDATA2LSB: - bytes = data[0] | - ((uint32_t)data[1] << 8) | - ((uint32_t)data[2] << 16) | - ((uint32_t)data[3] << 24); - break; - - // Big endian - case ELFDATA2MSB: - bytes = data[3] | - ((uint32_t)data[2] << 8) | - ((uint32_t)data[1] << 16) | - ((uint32_t)data[0] << 24); - break; - } - - return bytes; -} - -static int __pakfire_file_check_cf_protection_x86(struct pakfire_file* file, - const int endianess, const uint32_t type, const char* payload) { - switch (type) { - case GNU_PROPERTY_X86_FEATURE_1_AND: - break; - - // XXX what should we do in this case? Just ignore? - default: - return 0; - } - - uint32_t property = read_4_bytes(endianess, payload); - - // Check for IBT - if (!(property & GNU_PROPERTY_X86_FEATURE_1_IBT)) { - DEBUG(file->ctx, "%s: IBT property is not enabled\n", - pakfire_file_get_path(file)); - - // XXX TODO Actually modify any flags - - return 0; - } - - // Check for Shadow Stack - if (!(property & GNU_PROPERTY_X86_FEATURE_1_SHSTK)) { - DEBUG(file->ctx, "%s: Shadow Stack is not enabled\n", - pakfire_file_get_path(file)); - - // XXX TODO Actually modify any flags - - return 0; - } - - return 0; -} - -static int __pakfire_file_check_cf_protection_aarch64(struct pakfire_file* file, - const int endianess, const uint32_t type, const char* payload) { - # warning TODO - return 0; -} - -static int pakfire_file_check_cf_protection_callback(struct pakfire_file* file, - Elf* elf, const Elf_Scn* section, const GElf_Shdr* shdr, Elf_Data* data) { - GElf_Ehdr ehdr = {}; - GElf_Nhdr nhdr = {}; - size_t offset = 0; - size_t offset_name = 0; - size_t offset_data = 0; - int r; - - // Fetch the ELF header - if (!gelf_getehdr(elf, &ehdr)) { - ERROR(file->ctx, "Could not fetch the ELF header for %s: %m\n", - pakfire_file_get_path(file)); - return 1; - } - - // Fetch the endianess - const int endianess = ehdr.e_ident[EI_DATA]; - - // Fetch the .note header - offset = gelf_getnote(data, offset, &nhdr, &offset_name, &offset_data); - if (!offset) { - ERROR(file->ctx, "Could not read note section: %m\n"); - return 1; - } - - switch (nhdr.n_type) { - case NT_GNU_PROPERTY_TYPE_0: - break; - - // We are not interested in this here - default: - return 0; - } - -#if 0 - // Check if the name starts with "GNU" - if (nhdr.n_namesz == sizeof(ELF_NOTE_GNU) { - if (strcmp(data->d_buf + offset_name, ELF_NOTE_GNU) == 0) - break; - } -#endif - - size_t length = nhdr.n_descsz; - - // XXX Check if this is aligned to 8 bytes or 4 bytes on 32 bit - - const char* payload = (const char*)data->d_buf + offset_data; - - while (length) { - // Read the type and size of the .note section - const uint32_t type = read_4_bytes(endianess, payload); - const uint32_t size = read_4_bytes(endianess, payload + 4); - - // Skip header - length -= sizeof(type) + sizeof(size); - payload += sizeof(type) + sizeof(size); - - if (length < size) { - ERROR(file->ctx, "GNU Property note has an incorrect format\n"); - return 1; - } - - switch (ehdr.e_machine) { - case EM_AARCH64: - r = __pakfire_file_check_cf_protection_aarch64(file, endianess, type, payload); - if (r) - return r; - break; - - case EM_X86_64: - r = __pakfire_file_check_cf_protection_x86(file, endianess, type, payload); - if (r) - return r; - break; - - default: - ERROR(file->ctx, "Unsupported ELF type (%d)\n", ehdr.e_machine); - return 1; - } - - // Skip data part - length -= (size + 7) & ~7; - payload += (size + 7) & ~7; - } - - return 0; -} - -static int __pakfire_file_check_cf_protection(struct pakfire_file* file, Elf* elf, void* data) { - return pakfire_file_for_elf_section(file, elf, SHT_NOTE, pakfire_file_check_cf_protection_callback); -} - -static int pakfire_file_check_cf_protection(struct pakfire_file* file) { - return pakfire_file_open_elf(file, __pakfire_file_check_cf_protection, NULL); -} - int pakfire_file_check(struct pakfire_file* file, int* issues) { int r; @@ -2519,11 +2285,6 @@ int pakfire_file_check(struct pakfire_file* file, int* issues) { r = pakfire_file_check_debuginfo(file); if (r) return r; - - // Check CF protection - r = pakfire_file_check_cf_protection(file); - if (r) - return r; } DONE: diff --git a/src/libpakfire/linter-file.c b/src/libpakfire/linter-file.c index 7087aaf77..6b4e2eb33 100644 --- a/src/libpakfire/linter-file.c +++ b/src/libpakfire/linter-file.c @@ -248,6 +248,42 @@ static int pakfire_linter_file_get_elf_type(struct pakfire_linter_file* lfile) { return 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); + +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) { + Elf_Scn* section = NULL; + GElf_Shdr shdr; + Elf_Data* data; + int r = -EINVAL; + + // Walk through all sections + for (;;) { + section = elf_nextscn(elf, section); + if (!section) + break; + + // Fetch the section header + gelf_getshdr(section, &shdr); + + // Skip sections that don't match + if (type && shdr.sh_type != type) + continue; + + // Fetch the data + data = elf_getdata(section, NULL); + + // Call the callback + r = callback(lfile, elf, section, &shdr, data); + if (r) + break; + } + + return r; +} + + 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) { Elf_Scn* s = NULL; @@ -594,6 +630,174 @@ static int pakfire_linter_file_check_runpath(struct pakfire_linter_file* lfile) return pakfire_linter_file_elf(lfile, __pakfire_linter_file_check_runpath, NULL); } +static uint32_t read_4_bytes(const int endianess, const char* data) { + uint32_t bytes = 0; + + switch (endianess) { + // Litte Endian + case ELFDATA2LSB: + bytes = data[0] | + ((uint32_t)data[1] << 8) | + ((uint32_t)data[2] << 16) | + ((uint32_t)data[3] << 24); + break; + + // Big endian + case ELFDATA2MSB: + bytes = data[3] | + ((uint32_t)data[2] << 8) | + ((uint32_t)data[1] << 16) | + ((uint32_t)data[0] << 24); + break; + } + + return bytes; +} + +static int __pakfire_linter_file_check_cf_protection_aarch64(struct pakfire_linter_file* lfile, + const int endianess, const uint32_t type, const char* payload) { + # warning TODO + return 0; +} + +static int __pakfire_linter_file_check_cf_protection_riscv64(struct pakfire_linter_file* lfile, + const int endianess, const uint32_t type, const char* payload) { + # warning TODO + return 0; +} + +static int __pakfire_linter_file_check_cf_protection_x86(struct pakfire_linter_file* lfile, + const int endianess, const uint32_t type, const char* payload) { + switch (type) { + case GNU_PROPERTY_X86_FEATURE_1_AND: + break; + + // XXX what should we do in this case? Just ignore? + default: + return 0; + } + + uint32_t property = read_4_bytes(endianess, payload); + + // Check for IBT + if (!(property & GNU_PROPERTY_X86_FEATURE_1_IBT)) + return pakfire_linter_file_error(lfile, "Indirect Branch Tracking (IBT) is not enabled"); + + // Check for Shadow Stack + if (!(property & GNU_PROPERTY_X86_FEATURE_1_SHSTK)) + return pakfire_linter_file_error(lfile, "Shadow Stack is not enabled"); + + return 0; +} + +static int pakfire_linter_file_check_cf_protection_callback( + struct pakfire_linter_file* lfile, Elf* elf, + const Elf_Scn* section, const GElf_Shdr* shdr, Elf_Data* data) { + GElf_Ehdr ehdr = {}; + GElf_Nhdr nhdr = {}; + size_t offset = 0; + size_t offset_name = 0; + size_t offset_data = 0; + int r; + + // Fetch the ELF header + if (!gelf_getehdr(elf, &ehdr)) { + ERROR(lfile->ctx, "Could not fetch the ELF header for %s: %m\n", lfile->path); + return -errno; + } + + // Fetch the endianess + const int endianess = ehdr.e_ident[EI_DATA]; + + // Fetch the .note header + offset = gelf_getnote(data, offset, &nhdr, &offset_name, &offset_data); + if (!offset) { + ERROR(lfile->ctx, "Could not read note section: %m\n"); + return -errno; + } + + switch (nhdr.n_type) { + case NT_GNU_PROPERTY_TYPE_0: + break; + + // We are not interested in this here + default: + return 0; + } + +#if 0 + // Check if the name starts with "GNU" + if (nhdr.n_namesz == sizeof(ELF_NOTE_GNU) { + if (strcmp(data->d_buf + offset_name, ELF_NOTE_GNU) == 0) + break; + } +#endif + + size_t length = nhdr.n_descsz; + + // XXX Check if this is aligned to 8 bytes or 4 bytes on 32 bit + + const char* payload = (const char*)data->d_buf + offset_data; + + while (length) { + // Read the type and size of the .note section + const uint32_t type = read_4_bytes(endianess, payload); + const uint32_t size = read_4_bytes(endianess, payload + 4); + + // Skip header + length -= sizeof(type) + sizeof(size); + payload += sizeof(type) + sizeof(size); + + if (length < size) { + ERROR(lfile->ctx, "GNU Property note has an incorrect format\n"); + return -EINVAL; + } + + switch (ehdr.e_machine) { + case EM_AARCH64: + r = __pakfire_linter_file_check_cf_protection_aarch64( + lfile, endianess, type, payload); + if (r < 0) + return r; + break; + + case EM_RISCV: + r = __pakfire_linter_file_check_cf_protection_riscv64( + lfile, endianess, type, payload); + if (r < 0) + return r; + break; + + case EM_X86_64: + r = __pakfire_linter_file_check_cf_protection_x86( + lfile, endianess, type, payload); + if (r < 0) + return r; + break; + + default: + ERROR(lfile->ctx, "Unsupported ELF type (%d)\n", ehdr.e_machine); + return -ENOTSUP; + } + + // Skip data part + length -= (size + 7) & ~7; + payload += (size + 7) & ~7; + } + + 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); +} + int pakfire_linter_file_lint(struct pakfire_linter_file* lfile) { int r = 0; @@ -635,6 +839,11 @@ int pakfire_linter_file_lint(struct pakfire_linter_file* lfile) { r = pakfire_linter_file_check_runpath(lfile); if (r < 0) return r; + + // CF Protection + r = pakfire_linter_file_check_cf_protection(lfile); + if (r < 0) + return r; } return 0;