]> git.ipfire.org Git - pakfire.git/commitdiff
ELF: Move the CF Protection check
authorMichael Tremer <michael.tremer@ipfire.org>
Thu, 2 Jan 2025 14:52:28 +0000 (14:52 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Thu, 2 Jan 2025 14:52:28 +0000 (14:52 +0000)
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
src/libpakfire/elf.c
src/libpakfire/include/pakfire/elf.h
src/libpakfire/linter-file.c

index 268c2b3a55490adb50dd0cc35a25644c49c3d006..c5f70e96680ab6500803ed153cbf024234731af1 100644 (file)
@@ -210,6 +210,14 @@ int pakfire_elf_type(struct pakfire_elf* self) {
        return self->ehdr.e_type;
 }
 
+int pakfire_elf_machine(struct pakfire_elf* self) {
+       return self->ehdr.e_machine;
+}
+
+int pakfire_elf_endianess(struct pakfire_elf* self) {
+       return self->ehdr.e_ident[EI_DATA];
+}
+
 const char* pakfire_elf_build_id(struct pakfire_elf* self) {
        const void* buffer = NULL;
        ssize_t length;
@@ -286,6 +294,41 @@ static int pakfire_elf_get_section(struct pakfire_elf* self,
        return 1;
 }
 
+typedef int (*pakfire_elf_foreach_section_callback)(struct pakfire_elf* self,
+       const Elf_Scn* section, const GElf_Shdr* shdr, Elf_Data* data);
+
+static int pakfire_elf_foreach_section(struct pakfire_elf* self,
+               const Elf64_Word type, pakfire_elf_foreach_section_callback callback) {
+       Elf_Scn* section = NULL;
+       GElf_Shdr shdr;
+       Elf_Data* data;
+       int r = -EINVAL;
+
+       // Walk through all sections
+       for (;;) {
+               section = elf_nextscn(self->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(self, section, &shdr, data);
+               if (r)
+                       break;
+       }
+
+       return r;
+}
+
 typedef int (*pakfire_elf_dyn_walk_callback)
        (struct pakfire_elf* self, const GElf_Shdr* shdr, const GElf_Dyn* dyn, void* data);
 
@@ -480,6 +523,180 @@ int pakfire_elf_is_partially_relro(struct pakfire_elf* self) {
        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_elf_check_cf_protection_aarch64(struct pakfire_elf* self,
+               const int endianess, const uint32_t type, const char* payload) {
+       int flags = 0;
+
+       switch (type) {
+               case GNU_PROPERTY_AARCH64_FEATURE_1_AND:
+                       break;
+
+               // Ignore the rest
+               default:
+                       return 0;
+       }
+
+       uint32_t property = read_4_bytes(endianess, payload);
+
+       // Check for BTI
+       if (!(property & GNU_PROPERTY_AARCH64_FEATURE_1_BTI))
+               flags |= PAKFIRE_ELF_MISSING_BTI;
+
+       // Check for PAC
+       if (!(property & GNU_PROPERTY_AARCH64_FEATURE_1_PAC))
+               flags |= PAKFIRE_ELF_MISSING_PAC;
+
+       return 0;
+}
+
+static int pakfire_elf_check_cf_protection_riscv64(struct pakfire_elf* self,
+               const int endianess, const uint32_t type, const char* payload) {
+       // There is nothing to do here
+       return 0;
+}
+
+static int pakfire_elf_check_cf_protection_x86_64(struct pakfire_elf* self,
+               const int endianess, const uint32_t type, const char* payload) {
+       int flags = 0;
+
+       switch (type) {
+               case GNU_PROPERTY_X86_FEATURE_1_AND:
+                       break;
+
+               // Ignore the rest
+               default:
+                       return 0;
+       }
+
+       uint32_t property = read_4_bytes(endianess, payload);
+
+       // Check for IBT
+       if (!(property & GNU_PROPERTY_X86_FEATURE_1_IBT))
+               flags |= PAKFIRE_ELF_MISSING_IBT;
+
+       // Check for Shadow Stack
+       if (!(property & GNU_PROPERTY_X86_FEATURE_1_SHSTK))
+               flags |= PAKFIRE_ELF_MISSING_SHSTK;
+
+       return 0;
+}
+
+static int pakfire_elf_check_cf_protection(struct pakfire_elf* self,
+               const Elf_Scn* section, const GElf_Shdr* shdr, Elf_Data* data) {
+       GElf_Nhdr nhdr = {};
+       size_t offset = 0;
+       size_t offset_name = 0;
+       size_t offset_data = 0;
+       int r;
+
+       // Fetch the .note header
+       offset = gelf_getnote(data, offset, &nhdr, &offset_name, &offset_data);
+       if (!offset) {
+               ERROR(self->ctx, "Could not read note section: %m\n");
+               return -errno;
+       }
+
+       // Skip any other note headers
+       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
+
+       // Fetch the endianess
+       const int endianess = pakfire_elf_endianess(self);
+
+       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(self->ctx, "GNU Property note has an incorrect format\n");
+                       return -EINVAL;
+               }
+
+               switch (pakfire_elf_machine(self)) {
+                       case EM_AARCH64:
+                               r = pakfire_elf_check_cf_protection_aarch64(self, endianess, type, payload);
+                               if (r < 0)
+                                       return r;
+                               break;
+
+                       case EM_RISCV:
+                               r = pakfire_elf_check_cf_protection_riscv64(self, endianess, type, payload);
+                               if (r < 0)
+                                       return r;
+                               break;
+
+                       case EM_X86_64:
+                               r = pakfire_elf_check_cf_protection_x86_64(self, endianess, type, payload);
+                               if (r < 0)
+                                       return r;
+                               break;
+
+                       default:
+                               ERROR(self->ctx, "Unsupported ELF type (%d)\n", pakfire_elf_machine(self));
+                               return -ENOTSUP;
+               }
+
+               // Skip data part
+               length  -= (size + 7) & ~7;
+               payload += (size + 7) & ~7;
+       }
+
+       return 0;
+}
+
+int pakfire_elf_has_cf_protection(struct pakfire_elf* self) {
+       return pakfire_elf_foreach_section(self, SHT_NOTE, pakfire_elf_check_cf_protection);
+}
+
 int pakfire_elf_is_stripped(struct pakfire_elf* self) {
        Elf_Scn* symtab = NULL;
 
index d36f52b48539ade4253d6059f8c2842af5cdc7e3..9f6261e21cb15c8d199146c2fd6d7a028cf27226 100644 (file)
@@ -39,6 +39,8 @@ struct pakfire_elf* pakfire_elf_unref(struct pakfire_elf* self);
 
 const char* pakfire_elf_path(struct pakfire_elf* self);
 int pakfire_elf_type(struct pakfire_elf* self);
+int pakfire_elf_machine(struct pakfire_elf* self);
+int pakfire_elf_endianess(struct pakfire_elf* self);
 const char* pakfire_elf_build_id(struct pakfire_elf* self);
 const char* pakfire_elf_debuglink(struct pakfire_elf* self);
 
@@ -47,8 +49,17 @@ int pakfire_elf_has_ssp(struct pakfire_elf* self);
 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_is_stripped(struct pakfire_elf* self);
 
+// Return bitmap for pakfire_elf_has_cf_protection()
+enum {
+       PAKFIRE_ELF_MISSING_BTI   = (1 << 0),
+       PAKFIRE_ELF_MISSING_PAC   = (1 << 1),
+       PAKFIRE_ELF_MISSING_IBT   = (1 << 2),
+       PAKFIRE_ELF_MISSING_SHSTK = (1 << 3),
+};
+
 #endif /* PAKFIRE_PRIVATE */
 
 #endif /* PAKFIRE_ELF_H */
index fb2f7ace55d7edcbb5d176c76f79ee19e2925969..f8ff6a501151036f0099da10ed90d13f2d177d97 100644 (file)
@@ -346,42 +346,6 @@ static int pakfire_linter_check_script_interpreter(struct pakfire_linter_file* l
        return 0;
 }
 
-typedef int (*__pakfire_linter_file_for_elf_section_callback)(struct pakfire_linter_file* lfile,
-       const Elf_Scn* section, const GElf_Shdr* shdr, Elf_Data* data);
-
-static int pakfire_linter_file_for_elf_section(struct pakfire_linter_file* lfile,
-               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(lfile->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, section, &shdr, data);
-               if (r)
-                       break;
-       }
-
-       return r;
-}
-
-
 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;
@@ -571,186 +535,43 @@ static int pakfire_linter_file_check_runpath(struct pakfire_linter_file* lfile)
                lfile, __pakfire_linter_file_process_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) {
-       switch (type) {
-               case GNU_PROPERTY_AARCH64_FEATURE_1_AND:
-                       break;
-
-               // Ignore the rest
-               default:
-                       return 0;
-       }
-
-       uint32_t property = read_4_bytes(endianess, payload);
-
-       // Check for BTI
-       if (!(property & GNU_PROPERTY_AARCH64_FEATURE_1_BTI))
-               return pakfire_linter_file_error(lfile, "Branch Target Identification (BTI) is not enabled");
-
-       // Check for PAC
-       if (!(property & GNU_PROPERTY_AARCH64_FEATURE_1_PAC))
-               return pakfire_linter_file_error(lfile, "Pointer Authentication (PAC) is not enabled");
-
-       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) {
-       // There is nothing to do here
-       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;
-
-               // Ignore the rest
-               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,
-               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;
+static int pakfire_linter_file_check_cf_protection(struct pakfire_linter_file* lfile) {
        int r;
 
-       // Fetch the ELF header
-       if (!gelf_getehdr(lfile->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 if CF Protection has been enabled
+       int flags = pakfire_elf_has_cf_protection(lfile->_elf);
 
-       // 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;
+       // aarch64: Branch Target Identification
+       if (flags & PAKFIRE_ELF_MISSING_BTI) {
+               r = pakfire_linter_file_error(lfile, "Branch Target Identification (BTI) is not enabled");
+               if (r < 0)
+                       return r;
        }
 
-       switch (nhdr.n_type) {
-               case NT_GNU_PROPERTY_TYPE_0:
-                       break;
-
-               // We are not interested in this here
-               default:
-                       return 0;
+       // aarch64: Pointer Authentication
+       if (flags & PAKFIRE_ELF_MISSING_PAC) {
+               r = pakfire_linter_file_error(lfile, "Pointer Authentication (PAC) is not enabled");
+               if (r < 0)
+                       return r;
        }
 
-#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;
+       // x86_64: Indirect Branch Tracking
+       if (flags & PAKFIRE_ELF_MISSING_IBT) {
+               r = pakfire_linter_file_error(lfile, "Indirect Branch Tracking (IBT) is not enabled");
+               if (r < 0)
+                       return r;
        }
-#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;
+       // x86_64: Shadow Stack
+       if (flags & PAKFIRE_ELF_MISSING_SHSTK) {
+               r = pakfire_linter_file_error(lfile, "Shadow Stack is not enabled");
+               if (r < 0)
+                       return r;
        }
 
        return 0;
 }
 
-static int pakfire_linter_file_check_cf_protection(struct pakfire_linter_file* lfile) {
-       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) {
        switch (pakfire_elf_type(lfile->_elf)) {
                // Do not check Relocatable Objects