]> git.ipfire.org Git - pakfire.git/commitdiff
file: Implement scaffolding for CF protection check
authorMichael Tremer <michael.tremer@ipfire.org>
Thu, 13 Apr 2023 13:43:40 +0000 (13:43 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Thu, 13 Apr 2023 13:45:37 +0000 (13:45 +0000)
See #13084

Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
src/libpakfire/file.c

index 819587ef0138f20042a1b65f13044c2640a473ba..07a5da966df3765a60095309f341615ec81c0fe1 100644 (file)
@@ -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: