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;
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);
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;
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;
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