summaryrefslogtreecommitdiff
path: root/src/fips.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/fips.c')
-rw-r--r--src/fips.c167
1 files changed, 96 insertions, 71 deletions
diff --git a/src/fips.c b/src/fips.c
index 134d0eae..d798d577 100644
--- a/src/fips.c
+++ b/src/fips.c
@@ -593,22 +593,20 @@ run_random_selftests (void)
# endif
#define HMAC_LEN 32
-static const unsigned char __attribute__ ((section (".rodata1")))
-hmac_for_the_implementation[HMAC_LEN];
-
/*
- * In the ELF file opened as FP, determine the offset of the given
- * virtual address ADDR and return it in R_OFFSET1. Determine the
- * offset of last loadable section in R_OFFSET2. Rewinds FP to the
- * beginning on success.
+ * In the ELF file opened as FP, fill the ELF header to the pointer
+ * EHDR_P, determine the offset of last loadable segment in R_OFFSET.
+ * Also, find the section which contains the hmac value and return it
+ * in HMAC. Rewinds FP to the beginning on success.
*/
static gpg_error_t
-get_file_offsets (FILE *fp, unsigned long addr, ElfW (Ehdr) *ehdr_p,
- unsigned long *r_offset1, unsigned long *r_offset2)
+get_file_offset (FILE *fp, ElfW (Ehdr) *ehdr_p,
+ unsigned long *r_offset, unsigned char hmac[HMAC_LEN])
{
ElfW (Phdr) phdr;
- uint16_t e_phidx;
- long pos = 0;
+ ElfW (Shdr) shdr;
+ int i;
+ unsigned long off_segment = 0;
/* Read the ELF header */
if (fseek (fp, 0, SEEK_SET) != 0)
@@ -616,12 +614,6 @@ get_file_offsets (FILE *fp, unsigned long addr, ElfW (Ehdr) *ehdr_p,
if (fread (ehdr_p, sizeof (*ehdr_p), 1, fp) != 1)
return gpg_error_from_syserror ();
- /* Fix up the ELF header, clean all section information. */
- ehdr_p->e_shoff = 0;
- ehdr_p->e_shentsize = 0;
- ehdr_p->e_shnum = 0;
- ehdr_p->e_shstrndx = 0;
-
/* The program header entry size should match the size of the phdr struct */
if (ehdr_p->e_phentsize != sizeof (phdr))
return gpg_error (GPG_ERR_INV_OBJ);
@@ -632,11 +624,9 @@ get_file_offsets (FILE *fp, unsigned long addr, ElfW (Ehdr) *ehdr_p,
if (fseek (fp, ehdr_p->e_phoff, SEEK_SET) != 0)
return gpg_error_from_syserror ();
- /* Iterate over the program headers, compare their virtual addresses
- with the address we are looking for, and if the program header
- matches, calculate the offset of the given ADDR in the file using
- the program header's p_offset field. */
- for (e_phidx = 0; e_phidx < ehdr_p->e_phnum; e_phidx++)
+ /* Iterate over the program headers, determine the last loadable
+ segment. */
+ for (i = 0; i < ehdr_p->e_phnum; i++)
{
if (fread (&phdr, sizeof (phdr), 1, fp) != 1)
return gpg_error_from_syserror ();
@@ -647,45 +637,100 @@ get_file_offsets (FILE *fp, unsigned long addr, ElfW (Ehdr) *ehdr_p,
if (phdr.p_type != PT_LOAD)
break;
- pos = phdr.p_offset + phdr.p_filesz;
- if (phdr.p_vaddr <= addr && addr < phdr.p_vaddr + phdr.p_memsz)
- /* Found segment, compute the offset of ADDR in the file */
- *r_offset1 = phdr.p_offset + (addr - phdr.p_vaddr);
+ off_segment = phdr.p_offset + phdr.p_filesz;
}
- if (*r_offset1)
+ if (!off_segment)
+ /* The segment not found in the file */
+ return gpg_error (GPG_ERR_INV_OBJ);
+
+ /* The section header entry size should match the size of the shdr struct */
+ if (ehdr_p->e_shentsize != sizeof (shdr))
+ return gpg_error (GPG_ERR_INV_OBJ);
+ if (ehdr_p->e_shoff == 0)
+ return gpg_error (GPG_ERR_INV_OBJ);
+
+ /* Jump to the first section header */
+ if (fseek (fp, ehdr_p->e_shoff, SEEK_SET) != 0)
+ return gpg_error_from_syserror ();
+
+ /* Iterate over the section headers, determine the note section,
+ read the hmac value. */
+ for (i = 0; i < ehdr_p->e_shnum; i++)
{
- if (fseek (fp, 0, SEEK_SET) != 0)
+ long off;
+
+ if (fread (&shdr, sizeof (shdr), 1, fp) != 1)
return gpg_error_from_syserror ();
- *r_offset2 = (unsigned long)pos;
- return 0;
+ off = ftell (fp);
+ if (shdr.sh_type == SHT_NOTE && shdr.sh_flags == 0 && shdr.sh_size == 48)
+ {
+ const char header_of_the_note[] = {
+ 0x04, 0x00, 0x00, 0x00,
+ 0x20, 0x00, 0x00, 0x00,
+ 0xca, 0xfe, 0x2a, 0x8e,
+ 'F', 'D', 'O', 0x00
+ };
+ unsigned char header[16];
+
+ /* Jump to the note section. */
+ if (fseek (fp, shdr.sh_offset, SEEK_SET) != 0)
+ return gpg_error_from_syserror ();
+
+ if (fread (header, sizeof (header), 1, fp) != 1)
+ return gpg_error_from_syserror ();
+
+ if (!memcmp (header, header_of_the_note, 16))
+ {
+ /* Found. Read the hmac value into HMAC. */
+ if (fread (hmac, HMAC_LEN, 1, fp) != 1)
+ return gpg_error_from_syserror ();
+ break;
+ }
+
+ /* Back to the next section header. */
+ if (fseek (fp, off, SEEK_SET) != 0)
+ return gpg_error_from_syserror ();
+ }
}
- /* Segment not found in the file */
- return gpg_error (GPG_ERR_INV_OBJ);
+ if (i == ehdr_p->e_shnum)
+ /* The note section not found. */
+ return gpg_error (GPG_ERR_INV_OBJ);
+
+ /* Fix up the ELF header, clean all section information. */
+ ehdr_p->e_shoff = 0;
+ ehdr_p->e_shentsize = 0;
+ ehdr_p->e_shnum = 0;
+ ehdr_p->e_shstrndx = 0;
+
+ *r_offset = off_segment;
+ if (fseek (fp, 0, SEEK_SET) != 0)
+ return gpg_error_from_syserror ();
+
+ return 0;
}
static gpg_error_t
-hmac256_check (const char *filename, const char *key, struct link_map *lm)
+hmac256_check (const char *filename, const char *key)
{
gpg_error_t err;
FILE *fp;
gcry_md_hd_t hd;
- size_t buffer_size, nread;
+ const size_t buffer_size = 32768;
+ size_t nread;
char *buffer;
- unsigned long addr;
- unsigned long offset1 = 0;
- unsigned long offset2 = 0;
+ unsigned long offset = 0;
unsigned long pos = 0;
ElfW (Ehdr) ehdr;
+ unsigned char hmac[HMAC_LEN];
- addr = (unsigned long)hmac_for_the_implementation - lm->l_addr;
fp = fopen (filename, "rb");
if (!fp)
return gpg_error (GPG_ERR_INV_OBJ);
- err = get_file_offsets (fp, addr, &ehdr, &offset1, &offset2);
+ err = get_file_offset (fp, &ehdr, &offset, hmac);
if (err)
{
fclose (fp);
@@ -707,8 +752,7 @@ hmac256_check (const char *filename, const char *key, struct link_map *lm)
return err;
}
- buffer_size = 32768;
- buffer = xtrymalloc (buffer_size + HMAC_LEN);
+ buffer = xtrymalloc (buffer_size);
if (!buffer)
{
err = gpg_error_from_syserror ();
@@ -717,38 +761,21 @@ hmac256_check (const char *filename, const char *key, struct link_map *lm)
return err;
}
- nread = fread (buffer, 1, HMAC_LEN, fp);
- pos += nread;
- if (nread < HMAC_LEN)
- {
- xfree (buffer);
- fclose (fp);
- _gcry_md_close (hd);
- return gpg_error (GPG_ERR_TOO_SHORT);
- }
-
while (1)
{
- nread = fread (buffer+HMAC_LEN, 1, buffer_size, fp);
- if (pos + nread >= offset2)
- nread = offset2 - pos;
+ nread = fread (buffer, 1, buffer_size, fp);
+ if (pos + nread >= offset)
+ nread = offset - pos;
- /* Copy, fixed ELF header at the beginning. */
- if (pos - HMAC_LEN == 0)
+ /* Copy the fixed ELF header at the beginning. */
+ if (pos == 0)
memcpy (buffer, &ehdr, sizeof (ehdr));
+ _gcry_md_write (hd, buffer, nread);
+
if (nread < buffer_size)
- {
- if (pos - HMAC_LEN <= offset1 && offset1 <= pos + nread)
- memset (buffer + HMAC_LEN + offset1 - pos, 0, HMAC_LEN);
- _gcry_md_write (hd, buffer, nread+HMAC_LEN);
- break;
- }
+ break;
- if (pos - HMAC_LEN <= offset1 && offset1 <= pos + nread)
- memset (buffer + HMAC_LEN + offset1 - pos, 0, HMAC_LEN);
- _gcry_md_write (hd, buffer, nread);
- memcpy (buffer, buffer+buffer_size, HMAC_LEN);
pos += nread;
}
@@ -759,7 +786,7 @@ hmac256_check (const char *filename, const char *key, struct link_map *lm)
unsigned char *digest;
digest = _gcry_md_read (hd, 0);
- if (!memcmp (digest, hmac_for_the_implementation, HMAC_LEN))
+ if (!memcmp (digest, hmac, HMAC_LEN))
/* Success. */
err = 0;
else
@@ -780,13 +807,11 @@ check_binary_integrity (void)
gpg_error_t err;
Dl_info info;
const char *key = KEY_FOR_BINARY_CHECK;
- void *extra_info;
- if (!dladdr1 (hmac_for_the_implementation, &info, &extra_info,
- RTLD_DL_LINKMAP))
+ if (!dladdr (hmac256_check, &info))
err = gpg_error_from_syserror ();
else
- err = hmac256_check (info.dli_fname, key, extra_info);
+ err = hmac256_check (info.dli_fname, key);
reporter ("binary", 0, NULL, err? gpg_strerror (err):NULL);
#ifdef HAVE_SYSLOG