From: Michael Tremer Date: Thu, 13 Apr 2023 13:43:40 +0000 (+0000) Subject: file: Implement scaffolding for CF protection check X-Git-Tag: 0.9.29~196 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=dbae97eba3da74fa6cc19a8c0ebb793754be01bf;p=pakfire.git file: Implement scaffolding for CF protection check See #13084 Signed-off-by: Michael Tremer --- diff --git a/src/libpakfire/file.c b/src/libpakfire/file.c index 819587ef0..07a5da966 100644 --- a/src/libpakfire/file.c +++ b/src/libpakfire/file.c @@ -1964,6 +1964,45 @@ 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; + + // 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; @@ -2384,6 +2423,161 @@ 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->pakfire, "%s: IBT property is not enabled\n", file->path); + + // XXX TODO Actually modify any flags + + return 0; + } + + // Check for Shadow Stack + if (!(property & GNU_PROPERTY_X86_FEATURE_1_SHSTK)) { + DEBUG(file->pakfire, "%s: Shadow Stack is not enabled\n", file->path); + + // XXX TODO Actually modify any flags + + return 0; + } + + 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->pakfire, "Could not fetch the ELF header for %s: %m\n", file->path); + 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->pakfire, "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->pakfire, "GNU Property note has an incorrect format\n"); + return 1; + } + + switch (ehdr.e_machine) { +#if 0 + case EM_AARCH64: + r = __pakfire_file_check_cf_protection_aarch64(file, endianess, type, payload); + if (r) + return r; + break; +#endif + + case EM_X86_64: + r = __pakfire_file_check_cf_protection_x86(file, endianess, type, payload); + if (r) + return r; + break; + + default: + ERROR(file->pakfire, "Unsupported ELF type\n"); + 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; @@ -2445,6 +2639,11 @@ int pakfire_file_check(struct pakfire_file* file, int* issues) { r = pakfire_file_check_runpath(file); if (r) return r; + + // Check CF protection + r = pakfire_file_check_cf_protection(file); + if (r) + return r; } DONE: