lkml.org 
[lkml]   [2013]   [Jan]   [15]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    Date
    From
    Subject[PATCH 4/3] User space utility "signelf" to sign elf executable

    This is basic implementation of signelf utility. It can be used to sign an
    ELF executable file.

    A new section ".signature" is added to elf executable and signature of
    executable are put there.

    This is currently modeled on module signing and in fact imitates
    scripts/sign-file script in kernel sources.

    ELF parsing code I have taken from vmcore-dmesg.c in kexec project.

    Signed-off-by: Vivek Goyal <vgoyal@redhat.com>
    ---
    Makefile | 21 +
    signelf.c | 1130 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    2 files changed, 1151 insertions(+)

    Index: signelf/signelf.c
    ===================================================================
    --- /dev/null 1970-01-01 00:00:00.000000000 +0000
    +++ signelf/signelf.c 2013-01-18 02:27:41.000000000 -0500
    @@ -0,0 +1,1130 @@
    +/*
    + * signelf: Sign ELF executable
    + *
    + * Copyright (C) 2013 Vivek Goyal (vgoyal@redhat.com)
    + *
    + * This program is free software; you can redistribute it and/or modify
    + * it under the terms of the GNU General Public License as published by
    + * the Free Software Foundation (version 2 of the License).
    + *
    + * This program is distributed in the hope that it will be useful,
    + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    + * GNU General Public License for more details.
    + *
    + * You should have received a copy of the GNU General Public License
    + * along with this program; if not, write to the Free Software
    + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
    + */
    +
    +#define _BSD_SOURCE
    +
    +#include <stdio.h>
    +#include <stdlib.h>
    +#include <unistd.h>
    +#include <string.h>
    +#include <getopt.h>
    +#include <limits.h>
    +#include <sys/types.h>
    +#include <sys/stat.h>
    +#include <fcntl.h>
    +#include <errno.h>
    +#include <byteswap.h>
    +#include <elf.h>
    +#include <openssl/evp.h>
    +#include <openssl/pkcs7.h>
    +#include <openssl/bio.h>
    +#include <openssl/pem.h>
    +#include <openssl/x509.h>
    +#include <openssl/x509v3.h>
    +#include <openssl/err.h>
    +#include <openssl/rsa.h>
    +#include <openssl/asn1.h>
    +#include <endian.h>
    +
    +struct signelf_info {
    + char *in_file, *out_file, *privkey_file, *certificate_file;
    + Elf64_Ehdr ehdr;
    + Elf64_Phdr *phdr;
    + unsigned char md_value[EVP_MAX_MD_SIZE];
    + unsigned int md_len;
    + char *signer_name;
    + uint8_t *key_identifier;
    + unsigned int key_identifier_len;
    + /* Signature prefixed with signature len */
    + uint8_t *signature;
    + unsigned int signature_len;
    + /* Signature info */
    + char sig_info[64];
    + unsigned int sig_info_len;
    +};
    +
    +/* digest prefixed with algo identifier. Used as input file for signing */
    +char *temp_algo_ident_digest_file = "/tmp/signelf.algo_digest";
    +
    +/* signature file. output file of rsautl */
    +char *tempsig_file = "/tmp/signelf.sig";
    +
    +/*
    + * Contains the full signature (including info) which gets into a section.
    + * objcopy uses it
    + */
    +char *tempsigsection_file = "/tmp/signelf.sigsection";
    +
    +#define MD_BUF_SIZE 16384
    +
    +/* sha256 prologue */
    +static char algo_ident_sha256[] = {
    + 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09,
    + 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01,
    + 0x05, 0x00, 0x04, 0x20
    + };
    +static unsigned int algo_ident_sha256_len = 19;
    +
    +#if __BYTE_ORDER == __LITTLE_ENDIAN
    +#define ELFDATANATIVE ELFDATA2LSB
    +#elif __BYTE_ORDER == __BIG_ENDIAN
    +#define ELFDATANATIVE ELFDATA2MSB
    +#else
    +#error "Unknown machine endian"
    +#endif
    +
    +static uint16_t file16_to_cpu(struct signelf_info *sinfo, uint16_t val)
    +{
    + if (sinfo->ehdr.e_ident[EI_DATA] != ELFDATANATIVE)
    + val = bswap_16(val);
    + return val;
    +}
    +
    +static uint32_t file32_to_cpu(struct signelf_info *sinfo, uint32_t val)
    +{
    + if (sinfo->ehdr.e_ident[EI_DATA] != ELFDATANATIVE)
    + val = bswap_32(val);
    + return val;
    +}
    +
    +static uint64_t file64_to_cpu(struct signelf_info *sinfo, uint64_t val)
    +{
    + if (sinfo->ehdr.e_ident[EI_DATA] != ELFDATANATIVE)
    + val = bswap_64(val);
    + return val;
    +}
    +
    +static int read_elf32(struct signelf_info *sinfo, int fd)
    +{
    + Elf32_Ehdr ehdr32;
    + Elf32_Phdr *phdr32;
    + size_t phdrs32_size;
    + ssize_t ret = 0, i;
    +
    + ret = pread(fd, &ehdr32, sizeof(ehdr32), 0);
    + if (ret != sizeof(ehdr32)) {
    + fprintf(stderr, "Read of Elf header failed: %s\n",
    + strerror(errno));
    + return 1;
    + }
    +
    + sinfo->ehdr.e_type = file16_to_cpu(sinfo, ehdr32.e_type);
    + sinfo->ehdr.e_machine = file16_to_cpu(sinfo, ehdr32.e_machine);
    + sinfo->ehdr.e_version = file32_to_cpu(sinfo, ehdr32.e_version);
    + sinfo->ehdr.e_entry = file32_to_cpu(sinfo, ehdr32.e_entry);
    + sinfo->ehdr.e_phoff = file32_to_cpu(sinfo, ehdr32.e_phoff);
    + sinfo->ehdr.e_shoff = file32_to_cpu(sinfo, ehdr32.e_shoff);
    + sinfo->ehdr.e_flags = file32_to_cpu(sinfo, ehdr32.e_flags);
    + sinfo->ehdr.e_ehsize = file16_to_cpu(sinfo, ehdr32.e_ehsize);
    + sinfo->ehdr.e_phentsize= file16_to_cpu(sinfo, ehdr32.e_phentsize);
    + sinfo->ehdr.e_phnum = file16_to_cpu(sinfo, ehdr32.e_phnum);
    + sinfo->ehdr.e_shentsize= file16_to_cpu(sinfo, ehdr32.e_shentsize);
    + sinfo->ehdr.e_shnum = file16_to_cpu(sinfo, ehdr32.e_shnum);
    + sinfo->ehdr.e_shstrndx = file16_to_cpu(sinfo, ehdr32.e_shstrndx);
    +
    + if (sinfo->ehdr.e_version != EV_CURRENT) {
    + fprintf(stderr, "Bad Elf header version %u\n",
    + sinfo->ehdr.e_version);
    + return 1;
    + }
    + if (sinfo->ehdr.e_phentsize != sizeof(Elf32_Phdr)) {
    + fprintf(stderr, "Bad Elf progra header size %u expected %zu\n",
    + sinfo->ehdr.e_phentsize, sizeof(Elf32_Phdr));
    + return 1;
    + }
    + phdrs32_size = sinfo->ehdr.e_phnum * sizeof(Elf32_Phdr);
    + phdr32 = calloc(sinfo->ehdr.e_phnum, sizeof(Elf32_Phdr));
    + if (!phdr32) {
    + fprintf(stderr, "Calloc of %u phdrs32 failed: %s\n",
    + sinfo->ehdr.e_phnum, strerror(errno));
    + return 1;
    + }
    +
    + sinfo->phdr = calloc(sinfo->ehdr.e_phnum, sizeof(Elf64_Phdr));
    + if (!sinfo->phdr) {
    + fprintf(stderr, "Calloc of %u phdrs failed: %s\n",
    + sinfo->ehdr.e_phnum, strerror(errno));
    + ret = 1;
    + goto out_free_phdr32;
    + }
    + ret = pread(fd, phdr32, phdrs32_size, sinfo->ehdr.e_phoff);
    + if (ret < 0 || (size_t)ret != phdrs32_size) {
    + fprintf(stderr, "Read of program header @ 0x%llu for %zu bytes failed: %s\n",
    + (unsigned long long)sinfo->ehdr.e_phoff, phdrs32_size, strerror(errno));
    + ret = 1;
    + goto out_free_phdr;
    + }
    + for (i = 0; i < sinfo->ehdr.e_phnum; i++) {
    + sinfo->phdr[i].p_type = file32_to_cpu(sinfo, phdr32[i].p_type);
    + sinfo->phdr[i].p_offset = file32_to_cpu(sinfo,
    + phdr32[i].p_offset);
    + sinfo->phdr[i].p_vaddr = file32_to_cpu(sinfo,
    + phdr32[i].p_vaddr);
    + sinfo->phdr[i].p_paddr = file32_to_cpu(sinfo,
    + phdr32[i].p_paddr);
    + sinfo->phdr[i].p_filesz = file32_to_cpu(sinfo,
    + phdr32[i].p_filesz);
    + sinfo->phdr[i].p_memsz = file32_to_cpu(sinfo,
    + phdr32[i].p_memsz);
    + sinfo->phdr[i].p_flags = file32_to_cpu(sinfo,
    + phdr32[i].p_flags);
    + sinfo->phdr[i].p_align = file32_to_cpu(sinfo,
    + phdr32[i].p_align);
    + }
    + free(phdr32);
    + return ret;
    +
    +out_free_phdr:
    + free(sinfo->phdr);
    +out_free_phdr32:
    + free(phdr32);
    + return ret;
    +}
    +
    +static int read_elf64(struct signelf_info *sinfo, int fd)
    +{
    + Elf64_Ehdr ehdr64;
    + Elf64_Phdr *phdr64;
    + size_t phdrs_size;
    + ssize_t ret, i;
    +
    + ret = pread(fd, &ehdr64, sizeof(ehdr64), 0);
    + if (ret < 0 || (size_t)ret != sizeof(sinfo->ehdr)) {
    + fprintf(stderr, "Read of Elf header failed: %s\n",
    + strerror(errno));
    + return 1;
    + }
    +
    + sinfo->ehdr.e_type = file16_to_cpu(sinfo, ehdr64.e_type);
    + sinfo->ehdr.e_machine = file16_to_cpu(sinfo, ehdr64.e_machine);
    + sinfo->ehdr.e_version = file32_to_cpu(sinfo, ehdr64.e_version);
    + sinfo->ehdr.e_entry = file64_to_cpu(sinfo, ehdr64.e_entry);
    + sinfo->ehdr.e_phoff = file64_to_cpu(sinfo, ehdr64.e_phoff);
    + sinfo->ehdr.e_shoff = file64_to_cpu(sinfo, ehdr64.e_shoff);
    + sinfo->ehdr.e_flags = file32_to_cpu(sinfo, ehdr64.e_flags);
    + sinfo->ehdr.e_ehsize = file16_to_cpu(sinfo, ehdr64.e_ehsize);
    + sinfo->ehdr.e_phentsize = file16_to_cpu(sinfo, ehdr64.e_phentsize);
    + sinfo->ehdr.e_phnum = file16_to_cpu(sinfo, ehdr64.e_phnum);
    + sinfo->ehdr.e_shentsize = file16_to_cpu(sinfo, ehdr64.e_shentsize);
    + sinfo->ehdr.e_shnum = file16_to_cpu(sinfo, ehdr64.e_shnum);
    + sinfo->ehdr.e_shstrndx = file16_to_cpu(sinfo, ehdr64.e_shstrndx);
    +
    + if (sinfo->ehdr.e_version != EV_CURRENT) {
    + fprintf(stderr, "Bad Elf header version %u\n",
    + sinfo->ehdr.e_version);
    + return 1;
    + }
    + if (sinfo->ehdr.e_phentsize != sizeof(Elf64_Phdr)) {
    + fprintf(stderr, "Bad Elf progra header size %u expected %zu\n",
    + sinfo->ehdr.e_phentsize, sizeof(Elf64_Phdr));
    + return 1;
    + }
    + phdrs_size = sinfo-> ehdr.e_phnum * sizeof(Elf64_Phdr);
    + phdr64 = calloc(sinfo->ehdr.e_phnum, sizeof(Elf64_Phdr));
    + if (!phdr64) {
    + fprintf(stderr, "Calloc of %u phdrs64 failed: %s\n",
    + sinfo->ehdr.e_phnum, strerror(errno));
    + return 1;
    + }
    + sinfo->phdr = calloc(sinfo->ehdr.e_phnum, sizeof(Elf64_Phdr));
    + if (!sinfo->phdr) {
    + fprintf(stderr, "Calloc of %u phdrs failed: %s\n",
    + sinfo->ehdr.e_phnum, strerror(errno));
    + ret = 1;
    + goto out_free_phdr64;
    + }
    + ret = pread(fd, phdr64, phdrs_size, sinfo->ehdr.e_phoff);
    + if (ret < 0 || (size_t)ret != phdrs_size) {
    + fprintf(stderr, "Read of program header @ %llu for %zu bytes failed: %s\n",
    + (unsigned long long)(sinfo->ehdr.e_phoff), phdrs_size, strerror(errno));
    + ret = 1;
    + goto out_free_phdr;
    + }
    + for (i = 0; i < sinfo->ehdr.e_phnum; i++) {
    + sinfo->phdr[i].p_type = file32_to_cpu(sinfo, phdr64[i].p_type);
    + sinfo->phdr[i].p_flags = file32_to_cpu(sinfo,
    + phdr64[i].p_flags);
    + sinfo->phdr[i].p_offset = file64_to_cpu(sinfo,
    + phdr64[i].p_offset);
    + sinfo->phdr[i].p_vaddr = file64_to_cpu(sinfo,
    + phdr64[i].p_vaddr);
    + sinfo->phdr[i].p_paddr = file64_to_cpu(sinfo,
    + phdr64[i].p_paddr);
    + sinfo->phdr[i].p_filesz = file64_to_cpu(sinfo,
    + phdr64[i].p_filesz);
    + sinfo->phdr[i].p_memsz = file64_to_cpu(sinfo,
    + phdr64[i].p_memsz);
    + sinfo->phdr[i].p_align = file64_to_cpu(sinfo,
    + phdr64[i].p_align);
    + }
    + free(phdr64);
    + return ret;
    +
    +out_free_phdr:
    + free(sinfo->phdr);
    +out_free_phdr64:
    + free(phdr64);
    + return ret;
    +}
    +
    +#ifdef DEBUG
    +static void print_digest(struct signelf_info *sinfo)
    +{
    + int i;
    +
    + printf("Digest is: ");
    + for(i = 0; i < sinfo->md_len; i++)
    + printf("%02x", sinfo->md_value[i]);
    + printf("\n");
    +}
    +#endif
    +
    +/*
    + * First PT_LOAD segment might have ELF header mapped already. Handle it
    + * differently
    + */
    +static int calculate_digest_first_seg(struct signelf_info *sinfo,
    + int fd, int phdr_idx, EVP_MD_CTX *mdctx)
    +{
    + unsigned char buf[MD_BUF_SIZE];
    + loff_t offset;
    + size_t to_read;
    + unsigned int buf_len;
    + size_t sz = 0, sz_done = 0, sz_rem = 0;
    + unsigned int bytes;
    + unsigned int off_e_shoff, off_e_flags, off_e_shnum;
    +
    + if (sinfo->ehdr.e_ident[EI_CLASS] == ELFCLASS32) {
    + off_e_shoff = offsetof(Elf32_Ehdr, e_shoff);
    + off_e_flags = offsetof(Elf32_Ehdr, e_flags);
    + off_e_shnum = offsetof(Elf32_Ehdr, e_shnum);
    + } else {
    + off_e_shoff = offsetof(Elf64_Ehdr, e_shoff);
    + off_e_flags = offsetof(Elf64_Ehdr, e_flags);
    + off_e_shnum = offsetof(Elf64_Ehdr, e_shnum);
    + }
    +
    + /*
    + * Make sure ELF header is mapped into first PT_LOAD segment
    + * otherwise error out
    + */
    + if (sinfo->phdr[phdr_idx].p_offset != 0) {
    + fprintf(stderr, "Error: ELF header is not mapped"
    + " in first PT_LOAD segment\n");
    + return 1;
    + }
    +
    + /*
    + * First digest of ELF header. Except following e_shoff, e_shnum,
    + * and e_shstrndx.
    + */
    +
    + offset = 0;
    + to_read = off_e_shoff;
    +
    + buf_len = pread(fd, (void*)buf, to_read, offset);
    + if (buf_len != to_read) {
    + fprintf(stderr, "Failed to read %lu bytes.\n", to_read);
    + return 1;
    + }
    +
    + EVP_DigestUpdate(mdctx, buf, buf_len);
    + bytes = buf_len;
    +
    + /* Calculate digest of rest of the elf header */
    + offset = off_e_flags;
    + to_read = off_e_shnum - off_e_flags;
    + buf_len = pread(fd, (void*)buf, to_read, offset);
    + if (buf_len != to_read) {
    + fprintf(stderr, "Failed to read %lu bytes.\n", to_read);
    + return 1;
    + }
    +
    + EVP_DigestUpdate(mdctx, buf, buf_len);
    + bytes += buf_len;
    +
    + /* Calculate digest of rest of the segment */
    + offset = sinfo->ehdr.e_ehsize;
    + sz = sinfo->phdr[phdr_idx].p_filesz - sinfo->ehdr.e_ehsize;
    +
    + sz_rem = sz;
    + to_read = MD_BUF_SIZE;
    + sz_done = 0;
    +
    + while (sz_rem) {
    + if (sz_rem < to_read)
    + to_read = sz_rem;
    +
    + buf_len = pread(fd, (void*)buf, to_read, offset);
    + if (buf_len == -1) {
    + fprintf(stderr, "Failed to read file:%s\n",
    + strerror(errno));
    + return 1;
    + }
    +
    + /* TODO: Use fread() instead of pread() */
    + if (buf_len != to_read) {
    + fprintf(stderr, "Failed to read %lu bytes from"
    + " file :%s. Read %u bytes\n",
    + to_read, strerror(errno), buf_len);
    + return 1;
    + }
    +
    + if (buf_len == 0)
    + break;
    +
    + EVP_DigestUpdate(mdctx, buf, buf_len);
    +
    + bytes += buf_len;
    + sz_rem -= buf_len;
    + sz_done += buf_len;
    + offset += buf_len;
    +
    + to_read = sz_rem;
    + if (to_read > MD_BUF_SIZE)
    + to_read = MD_BUF_SIZE;
    + }
    +
    + if (sz_done != sz) {
    + fprintf(stderr, "Could not digest %lu bytes. Digested"
    + " only %lu bytes\n", sz, sz_done);
    + return 1;
    + }
    +
    + return 0;
    +}
    +
    +static int calculate_elf_hash(struct signelf_info *sinfo, int fd)
    +{
    + EVP_MD_CTX *mdctx;
    + const EVP_MD *md;
    + unsigned int buf_len;
    + unsigned char buf[MD_BUF_SIZE];
    + int i;
    + size_t sz = 0, sz_done = 0, sz_rem = 0;
    + int ret;
    + int first_segment = 1;
    +
    + OpenSSL_add_all_digests();
    +
    + /* TODO: Make digest type variable */
    + md = EVP_get_digestbyname("sha256");
    + mdctx = EVP_MD_CTX_create();
    + if (!mdctx) {
    + fprintf(stderr, "mdctx creation failed\n");
    + return 1;
    + }
    +
    + EVP_DigestInit_ex(mdctx, md, NULL);
    +
    + /*
    + * Now go through all PT_LOAD program header areas and calculate
    + * their checksum too. During verification, kernel will lock the
    + * process mapped areas (essentially these PT_LOAD segments) and
    + * go over these areas to calculate digest.
    + *
    + * Also calculate checksum of ELF header except two last fields.
    + * Number of section headers and section header string table
    + * index. Once signature is added in a section these two fields
    + * will change.
    + */
    + /* TODO: calculate hash of all program headers first */
    + for (i = 0; i < sinfo->ehdr.e_phnum; i++) {
    + loff_t offset;
    + size_t to_read;
    +
    + if (sinfo->phdr[i].p_type != PT_LOAD)
    + continue;
    +
    + if (first_segment) {
    + ret = calculate_digest_first_seg(sinfo, fd,
    + i, mdctx);
    + if (ret) {
    + fprintf(stderr, "Error while calculating"
    + " digest\n");
    + return 1;
    + }
    + first_segment = 0;
    + continue;
    + }
    +
    + /* Calcualte hash of PT_LOAD area contents */
    + /* Skip if segment size is 0 (bss) */
    + offset = sinfo->phdr[i].p_offset;
    + sz = sinfo->phdr[i].p_filesz;
    +
    + sz_rem = sz;
    + to_read = MD_BUF_SIZE;
    + sz_done = 0;
    +
    + while (sz_rem) {
    + if (sz_rem < to_read)
    + to_read = sz_rem;
    +
    + buf_len = pread(fd, (void*)buf, to_read, offset);
    + if (buf_len == -1) {
    + fprintf(stderr, "Failed to read:%s\n",
    + strerror(errno));
    + return 1;
    + }
    +
    + /* TODO: Use fread() instead of pread() */
    + if (buf_len != to_read) {
    + fprintf(stderr, "Failed to read %lu bytes."
    + " Read %u bytes:%s\n",
    + to_read, buf_len, strerror(errno));
    + return 1;
    + }
    +
    + if (buf_len == 0)
    + break;
    +
    + EVP_DigestUpdate(mdctx, buf, buf_len);
    +
    + sz_rem -= buf_len;
    + sz_done += buf_len;
    + offset += buf_len;
    +
    + to_read = sz_rem;
    + if (to_read > MD_BUF_SIZE)
    + to_read = MD_BUF_SIZE;
    + }
    +
    + if (sz_done != sz) {
    + fprintf(stderr, "Could not digest %lu bytes. Digested"
    + " only %lu bytes\n", sz, sz_done);
    + return 1;
    + }
    + }
    +
    + EVP_DigestFinal_ex(mdctx, sinfo->md_value, &sinfo->md_len);
    + EVP_MD_CTX_destroy(mdctx);
    + EVP_cleanup();
    +
    +#ifdef DEBUG
    + print_digest(sinfo);
    +#endif
    + return 0;
    +}
    +
    +static int prefix_algo_identifier_to_digest(struct signelf_info *sinfo)
    +{
    + FILE *algo_digest_fp;
    + size_t nr_write;
    +
    + algo_digest_fp = fopen(temp_algo_ident_digest_file, "w+");
    + if (!algo_digest_fp) {
    + fprintf(stderr, "Failed to open file %s\n",
    + temp_algo_ident_digest_file);
    + return 1;
    + }
    +
    + /* Write prologue */
    + nr_write = fwrite(algo_ident_sha256, 1, algo_ident_sha256_len,
    + algo_digest_fp);
    + if (nr_write != algo_ident_sha256_len) {
    + fprintf(stderr, "Failed to write prologue to %s\n",
    + temp_algo_ident_digest_file);
    + fclose(algo_digest_fp);
    + return 1;
    + }
    +
    + /* Write digest */
    + nr_write = fwrite(sinfo->md_value, 1, sinfo->md_len, algo_digest_fp);
    + if (nr_write != sinfo->md_len) {
    + fprintf(stderr, "Failed to write digest to %s\n",
    + temp_algo_ident_digest_file);
    + fclose(algo_digest_fp);
    + return 1;
    + }
    +
    + fclose(algo_digest_fp);
    + return 0;
    +}
    +
    +static int prefix_siglen_to_signature(struct signelf_info *sinfo)
    +{
    + char buf[4096];
    + FILE *sig_fp;
    + unsigned int nr_read, total_siglen;
    + uint16_t siglen, siglen_be16;
    +
    +
    + sig_fp = fopen(tempsig_file, "r");
    + if (!sig_fp) {
    + fprintf(stderr, "Failed to open file %s\n", tempsig_file);
    + return 1;
    + }
    +
    + /* Read signature from file */
    + nr_read = fread(buf, 1, 4096, sig_fp);
    + if (nr_read == 0 && ferror(sig_fp)) {
    + fprintf(stderr, "Failed to read file %s\n", tempsig_file);
    + fclose(sig_fp);
    + return 1;
    + }
    +
    + siglen = (uint16_t)nr_read;
    + siglen_be16 = htobe16(siglen);
    +
    + total_siglen = siglen + 2; // 2 byte for signature len
    +
    + /* allocate memory for signature */
    + sinfo->signature = malloc(total_siglen);
    + if (!sinfo->signature) {
    + fprintf(stderr, "Failed to allocated %d bytes\n", total_siglen);
    + fclose(sig_fp);
    + return 1;
    + }
    +
    + *(uint16_t*)sinfo->signature = siglen_be16;
    + memcpy(sinfo->signature + 2, buf, siglen);
    + sinfo->signature_len = total_siglen;
    +
    + fclose(sig_fp);
    + return 0;
    +}
    +
    +static int sign_hash_rsautl(struct signelf_info *sinfo)
    +{
    + int ret, exit_code;
    + char command[1024];
    +
    + snprintf(command, 1024, "openssl rsautl -sign -in %s -inkey %s -out %s",
    + temp_algo_ident_digest_file, sinfo->privkey_file,
    + tempsig_file);
    + ret = system(command);
    +
    + if (ret == -1) {
    + fprintf(stderr, "Failed to execute system(%s)\n", command);
    + return 1;
    + }
    +
    + exit_code = WEXITSTATUS(ret);
    + return exit_code;
    +}
    +
    +static int x509_get_subject_key_identifier(struct signelf_info *sinfo)
    +{
    + BIO *cert_buf;
    + X509 *cert_x509;
    + int ret = 0;
    + ASN1_OCTET_STRING *skid;
    +
    + cert_buf = BIO_new_file(sinfo->certificate_file, "r");
    + if (!cert_buf) {
    + fprintf(stderr, "BIO_new_file on %s failed\n",
    + sinfo->certificate_file);
    + ret = 1;
    + goto out;
    + }
    +
    + cert_x509 = d2i_X509_bio(cert_buf, NULL);
    + if (!cert_x509) {
    + fprintf(stderr, "Failed to parse DER format certificate\n");
    + ret = 1;
    + goto out_free_cert_buf;
    + }
    +
    + skid = X509_get_ext_d2i(cert_x509, NID_subject_key_identifier,
    + NULL, NULL);
    + if (!skid) {
    + fprintf(stderr, "Failed to find subject key identifier"
    + " extension in certificate\n");
    + ret = 1;
    + goto out_free_x509;
    + }
    +
    + sinfo->key_identifier = malloc(skid->length);
    + if (!sinfo->key_identifier) {
    + fprintf(stderr, "Failed to allocated %d bytes:%s\n",
    + skid->length, strerror(errno));
    + ret = 1;
    + goto out_free_asn1_string;
    + }
    +
    + memcpy(sinfo->key_identifier, skid->data, skid->length);
    + sinfo->key_identifier_len = skid->length;
    +
    +#ifdef DEBUG
    + {
    + int i;
    + printf("skid in hex format:\n");
    + for (i = 0; i < skid->length; i++) {
    + printf("%02X", skid->data[i]);
    + if (i < skid->length - 1)
    + printf(":");
    + }
    + printf("\n");
    + }
    +#endif
    +
    +out_free_asn1_string:
    + ASN1_STRING_free(skid);
    +out_free_x509:
    + X509_free(cert_x509);
    +out_free_cert_buf:
    + BIO_free(cert_buf);
    +out:
    + return ret;
    +}
    +
    +static int x509_get_subject(struct signelf_info *sinfo)
    +{
    + BIO *cert_buf;
    + X509 *cert_x509;
    + int ret = 0, len;
    + X509_NAME *subject_x509_name;
    + char *common_name = NULL, *org_name = NULL, *email_addr = NULL;
    + int common_name_len = 0, org_name_len = 0, email_addr_len = 0;
    +
    + cert_buf = BIO_new_file(sinfo->certificate_file, "r");
    + if (!cert_buf) {
    + fprintf(stderr, "BIO_new_file on %s failed\n",
    + sinfo->certificate_file);
    + ret = 1;
    + goto out;
    + }
    +
    + cert_x509 = d2i_X509_bio(cert_buf, NULL);
    + if (!cert_x509) {
    + fprintf(stderr, "Failed to parse DER format certificate\n");
    + ret = 1;
    + goto out_free_cert_buf;
    + }
    +
    + subject_x509_name = X509_get_subject_name(cert_x509);
    + if (!subject_x509_name) {
    + fprintf(stderr, "Failed to get subject name from"
    + " certificate\n");
    + ret = 1;
    + goto out_free_cert_buf;
    + }
    +
    + /* Get commonName */
    + len = X509_NAME_get_text_by_NID(subject_x509_name, NID_commonName,
    + NULL, 0);
    +
    + if (len != -1) {
    + common_name_len = len;
    + common_name = malloc(len + 1);
    + if (!common_name) {
    + fprintf(stderr, "Failed to allocate %d bytes of"
    + " memory:%s\n", len + 1, strerror(errno));
    + ret = 1;
    + goto out_free_cert_buf;
    + }
    +
    + len = X509_NAME_get_text_by_NID(subject_x509_name,
    + NID_commonName, common_name,
    + common_name_len + 1);
    +
    + if (len == -1) {
    + fprintf(stderr, "Failed to get commonName\n");
    + ret = 1;
    + goto out_free_common_name;
    + }
    + }
    +
    + /* Get orgName */
    + len = X509_NAME_get_text_by_NID(subject_x509_name, NID_organizationName,
    + NULL, 0);
    +
    + if (len != -1) {
    + org_name_len = len;
    + org_name = malloc(len + 1);
    + if (!org_name) {
    + fprintf(stderr, "Failed to allocate %d bytes of"
    + " memory:%s\n", len + 1, strerror(errno));
    + ret = 1;
    + goto out_free_common_name;
    + }
    +
    + len = X509_NAME_get_text_by_NID(subject_x509_name,
    + NID_organizationName, org_name,
    + org_name_len + 1);
    +
    + if (len == -1) {
    + fprintf(stderr, "Failed to get organizationName\n");
    + ret = 1;
    + goto out_free_org_name;
    + }
    + }
    +
    + /* Get email addr */
    +
    + len = X509_NAME_get_text_by_NID(subject_x509_name,
    + NID_pkcs9_emailAddress, NULL, 0);
    +
    + if (len != -1) {
    + email_addr_len = len;
    + email_addr = malloc(len + 1);
    + if (!email_addr) {
    + fprintf(stderr, "Failed to allocate %d bytes of"
    + " memory:%s\n", len + 1, strerror(errno));
    + ret = 1;
    + goto out_free_org_name;
    + }
    +
    + len = X509_NAME_get_text_by_NID(subject_x509_name,
    + NID_pkcs9_emailAddress, email_addr,
    + email_addr_len + 1);
    +
    + if (len == -1) {
    + fprintf(stderr, "Failed to get email addr\n");
    + ret = 1;
    + goto out_free_email_addr;
    + }
    + }
    +
    + sinfo->signer_name = NULL;
    +
    + if (common_name_len && org_name_len) {
    + /* If common name contains organization name, don't use it */
    + if (strstr(common_name, org_name)) {
    + sinfo->signer_name = strdup(common_name);
    + } else {
    + sinfo->signer_name = malloc(org_name_len + common_name_len + 3);
    + sprintf(sinfo->signer_name, "%s: %s", org_name,
    + common_name);
    + }
    + } else if (common_name_len) {
    + sinfo->signer_name = strdup(common_name);
    + } else if (org_name_len) {
    + sinfo->signer_name = strdup(org_name);
    + } else if(email_addr)
    + sinfo->signer_name = strdup(email_addr);
    +
    + if (!sinfo->signer_name) {
    + fprintf(stderr, "Could not obtain signer name\n");
    + ret = 1;
    + }
    +out_free_email_addr:
    + free(email_addr);
    +out_free_org_name:
    + free(org_name);
    +out_free_common_name:
    + free(common_name);
    +out_free_cert_buf:
    + BIO_free(cert_buf);
    +out:
    + return ret;
    +}
    +
    +static int prepare_sig_info(struct signelf_info *sinfo)
    +{
    + /* algo = 1 (RSA), hash = 4 (sha256), id_type=1 (X.509) */
    + unsigned int algo=1, hash = 4, id_type = 1, i=0;
    + uint8_t signer_name_len = strlen(sinfo->signer_name);
    + uint32_t signature_len_be = htobe32(sinfo->signature_len);
    +
    + sinfo->sig_info[i++] = algo;
    + sinfo->sig_info[i++] = hash;
    + sinfo->sig_info[i++] = id_type;
    + sinfo->sig_info[i++] = signer_name_len;
    + sinfo->sig_info[i++] = sinfo->key_identifier_len;
    + sinfo->sig_info[i++] = 0;
    + sinfo->sig_info[i++] = 0;
    + sinfo->sig_info[i++] = 0;
    +
    + sinfo->sig_info_len = i;
    + memcpy(&sinfo->sig_info[i], &signature_len_be, 4);
    + sinfo->sig_info_len += 4;
    +
    + return 0;
    +}
    +
    +static int add_signature_in_a_section(struct signelf_info *sinfo)
    +{
    + FILE *outfp;
    + int ret = 0, exit_code;
    + unsigned int written;
    + unsigned int signer_name_len;
    + char command[1024];
    +
    + outfp = fopen(tempsigsection_file, "w+");
    + if (!outfp) {
    + fprintf(stderr, "Failed to open %s:%s\n", tempsigsection_file,
    + strerror(errno));
    + return 1;
    + }
    +
    + /* Write signer's name */
    + signer_name_len = strlen(sinfo->signer_name);
    + written = fwrite(sinfo->signer_name, 1, signer_name_len, outfp);
    + if (written != signer_name_len) {
    + fprintf(stderr, "Failed to write signer name to file %s\n",
    + tempsigsection_file);
    + ret = 1;
    + goto out_close_outfp;
    + }
    +
    + /* Write skid */
    + written = fwrite(sinfo->key_identifier, 1, sinfo->key_identifier_len,
    + outfp);
    + if (written != sinfo->key_identifier_len) {
    + fprintf(stderr, "Failed to write key identifier to file %s\n",
    + tempsigsection_file);
    + ret = 1;
    + goto out_close_outfp;
    + }
    +
    + /* Write signature */
    + written = fwrite(sinfo->signature, 1, sinfo->signature_len, outfp);
    + if (written != sinfo->signature_len) {
    + fprintf(stderr, "Failed to write signature to file %s\n",
    + tempsigsection_file);
    + ret = 1;
    + goto out_close_outfp;
    + }
    +
    + /* Write info */
    + written = fwrite(sinfo->sig_info, 1, sinfo->sig_info_len, outfp);
    + if (written != sinfo->sig_info_len) {
    + fprintf(stderr, "Failed to write info to file %s\n",
    + tempsigsection_file);
    + ret = 1;
    + goto out_close_outfp;
    + }
    +
    + /* Add signature section */
    + fflush(outfp);
    + snprintf(command, 1024, "objcopy --add-section .signature=%s %s %s",
    + tempsigsection_file, sinfo->in_file, sinfo->out_file);
    + ret = system(command);
    + if (ret == -1) {
    + fprintf(stderr, "Failed to execute system(%s)\n", command);
    + goto out_close_outfp;
    + }
    +
    + exit_code = WEXITSTATUS(ret);
    + ret = exit_code;
    + if (ret)
    + goto out_close_outfp;
    +
    +out_close_outfp:
    + fclose(outfp);
    + return ret;
    +}
    +
    +static int sign_elf_executable(struct signelf_info *sinfo)
    +{
    + int ret, fd;
    +
    + fd = open(sinfo->in_file, O_RDONLY);
    + if (fd < 0) {
    + fprintf(stderr, "Cannot open %s: %s\n",
    + sinfo->in_file, strerror(errno));
    + return 1;
    + }
    +
    + ret = pread(fd, sinfo->ehdr.e_ident, EI_NIDENT, 0);
    + if (ret != EI_NIDENT) {
    + fprintf(stderr, "Read of e_ident from %s failed: %s\n",
    + sinfo->in_file, strerror(errno));
    + ret = 1;
    + goto out;
    + }
    +
    + if (memcmp(sinfo->ehdr.e_ident, ELFMAG, SELFMAG) != 0) {
    + fprintf(stderr, "Missing elf signature\n");
    + ret = 1;
    + goto out;
    + }
    +
    + if (sinfo->ehdr.e_ident[EI_VERSION] != EV_CURRENT) {
    + fprintf(stderr, "Bad elf version\n");
    + ret = 1;
    + goto out;
    + }
    +
    + if ((sinfo->ehdr.e_ident[EI_CLASS] != ELFCLASS32) &&
    + (sinfo->ehdr.e_ident[EI_CLASS] != ELFCLASS64))
    + {
    + fprintf(stderr, "Unknown elf class %u\n",
    + sinfo->ehdr.e_ident[EI_CLASS]);
    + ret = 1;
    + goto out;
    + }
    +
    + if ((sinfo->ehdr.e_ident[EI_DATA] != ELFDATA2LSB) &&
    + (sinfo->ehdr.e_ident[EI_DATA] != ELFDATA2MSB))
    + {
    + fprintf(stderr, "Unkown elf data order %u\n",
    + sinfo->ehdr.e_ident[EI_DATA]);
    + ret = 1;
    + goto out;
    + }
    +
    + if (sinfo->ehdr.e_ident[EI_CLASS] == ELFCLASS32)
    + ret = read_elf32(sinfo, fd);
    + else
    + ret = read_elf64(sinfo, fd);
    +
    + if (!ret)
    + goto out;
    +
    + ret = x509_get_subject(sinfo);
    + if (ret)
    + goto out;
    +
    + ret = x509_get_subject_key_identifier(sinfo);
    + if (ret)
    + goto out;
    +
    + if (calculate_elf_hash(sinfo, fd)) {
    + ret = 1;
    + goto out;
    + }
    +
    + ret = prefix_algo_identifier_to_digest(sinfo);
    + if (ret)
    + goto out;
    +
    + ret = sign_hash_rsautl(sinfo);
    + if (ret)
    + goto out;
    +
    + ret = prefix_siglen_to_signature(sinfo);
    + if (ret)
    + goto out;
    +
    + ret = prepare_sig_info(sinfo);
    + if (ret)
    + goto out;
    +
    + ret = add_signature_in_a_section(sinfo);
    + if (ret) {
    + fprintf(stderr, "Error while putting signature into an elf"
    + " section\n");
    + goto out;
    + }
    +
    +out:
    + close(fd);
    + return ret;
    +}
    +
    +static void print_help()
    +{
    + printf("Usage: signelf [OPTION...]\n");
    + printf(" -i, --in=<infile>\t\t\t\tspecify input file\n");
    + printf(" -p, --privkey=<privkey>\t\t\tspecify private key file\n");
    + printf(" -c, --certificate=<certificate>\t\tspecify certificate file\n");
    + printf(" -o, --out=<outfile>\t\t\t\tspecify output file\n");
    +}
    +
    +static void free_sinfo_members(struct signelf_info *sinfo)
    +{
    + free(sinfo->in_file);
    + free(sinfo->out_file);
    + free(sinfo->privkey_file);
    + free(sinfo->certificate_file);
    + free(sinfo->signer_name);
    + free(sinfo->key_identifier);
    + free(sinfo->signature);
    +}
    +
    +int main(int argc, char *argv[])
    +{
    + int ret = 0;
    + char *option_string = "hi:p:c:o:", c;
    + struct signelf_info *sinfo, signelf_info;
    +
    + struct option long_options[] =
    + {
    + {"help", no_argument, 0, 'h'},
    + {"in", required_argument, 0, 'i'},
    + {"privkey", required_argument, 0, 'p'},
    + {"certificate", required_argument, 0, 'c'},
    + {"out", required_argument, 0, 'o'},
    + { 0, 0, 0, 0}
    + };
    +
    + if (argc < 2) {
    + print_help();
    + exit(1);
    + }
    +
    + sinfo = &signelf_info;
    + memset(sinfo, 0, sizeof(struct signelf_info));
    +
    + while((c = getopt_long(argc, argv, option_string, &long_options[0],
    + NULL)) != -1) {
    + switch(c) {
    + case '?':
    + /* Unknown option or missing argument*/
    + print_help();
    + exit(1);
    + case 'h':
    + print_help();
    + exit(0);
    + case 'i':
    + sinfo->in_file = strdup(optarg);
    + if (!sinfo->in_file) {
    + fprintf(stderr, "Can't duplicate string:%s\n",
    + strerror(errno));
    + exit(1);
    + }
    + break;
    + case 'p':
    + sinfo->privkey_file = strdup(optarg);
    + if (!sinfo->privkey_file) {
    + fprintf(stderr, "Can't duplicate string:%s\n",
    + strerror(errno));
    + exit(1);
    + }
    + break;
    + case 'c':
    + sinfo->certificate_file = strdup(optarg);
    + if (!sinfo->certificate_file) {
    + fprintf(stderr, "Can't duplicate string:%s\n",
    + strerror(errno));
    + exit(1);
    + }
    + break;
    + case 'o':
    + sinfo->out_file = strdup(optarg);
    + if (!sinfo->out_file) {
    + fprintf(stderr, "Can't duplicate string:%s\n",
    + strerror(errno));
    + exit(1);
    + }
    + break;
    + default:
    + printf("Unexpected option\n");
    + exit(1);
    + }
    + }
    +
    + if (!sinfo->in_file || !sinfo->out_file || !sinfo->privkey_file ||
    + !sinfo->certificate_file) {
    + print_help();
    + exit(1);
    + }
    +
    + ret = sign_elf_executable(sinfo);
    +
    + free_sinfo_members(sinfo);
    + remove(temp_algo_ident_digest_file);
    + remove(tempsig_file);
    + remove(tempsigsection_file);
    +
    + exit(ret);
    +}
    Index: signelf/Makefile
    ===================================================================
    --- /dev/null 1970-01-01 00:00:00.000000000 +0000
    +++ signelf/Makefile 2013-01-18 02:27:41.000000000 -0500
    @@ -0,0 +1,21 @@
    +CC = gcc
    +OPTFLAGS= -g
    +CFLAGS = -Wall -D_GNU_SOURCE $(OPTFLAGS)
    +PROGS = signelf
    +OBJS = signelf.o
    +
    +INSTALL = install
    +prefix = /usr/local
    +bindir = $(prefix)/bin
    +
    +%.o: %.c
    + $(CC) -o $*.o -c $(CFLAGS) $<
    +signelf: $(OBJS)
    + $(CC) $(CFLAGS) -o $@ $(filter %.o,$^) -lcrypto
    +
    +install:
    + $(INSTALL) -m755 -d $(DESTDIR)$(bindir)
    + $(INSTALL) $(PROGS) $(DESTDIR)$(bindir)
    +
    +clean:
    + rm -f $(OBJS) $(PROGS)

    \
     
     \ /
      Last update: 2013-01-15 23:21    [W:4.208 / U:0.028 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site