From f6115a8045275a0dc138f9088ba018441146e81d Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Fri, 19 Dec 2014 16:10:34 -0800 Subject: [PATCH] sbsign, sbattach, sbverify: add multiple signature support sbsign will sign an already signed binary (adding a signature at the end) sbverify has a new mode --list, for listing all the signatures and sbattach takes a --signum argument for --remove or --detach. Signed-off-by: James Bottomley --- src/image.c | 145 ++++++++++++++++++++++++++++++++++--------------- src/image.h | 6 +- src/sbattach.c | 32 ++++++++--- src/sbsign.c | 12 +++- src/sbverify.c | 144 ++++++++++++++++++++++++------------------------ 5 files changed, 211 insertions(+), 128 deletions(-) diff --git a/src/image.c b/src/image.c index 519e288..9fdeecd 100644 --- a/src/image.c +++ b/src/image.c @@ -129,6 +129,11 @@ static int image_pecoff_parse_64(struct image *image) return 0; } +static int align_up(int size, int align) +{ + return (size + align - 1) & ~(align - 1); +} + static int image_pecoff_parse(struct image *image) { struct cert_table_header *cert_table; @@ -224,12 +229,12 @@ static int image_pecoff_parse(struct image *image) image->cert_table = cert_table; /* if we have a valid cert table header, populate sigbuf as a shadow - * copy of the cert table */ + * copy of the cert tables */ if (cert_table && cert_table->revision == CERT_TABLE_REVISION && cert_table->type == CERT_TABLE_TYPE_PKCS && cert_table->size < size) { - image->sigsize = cert_table->size; - image->sigbuf = talloc_memdup(image, cert_table + 1, + image->sigsize = image->data_dir_sigtable->size; + image->sigbuf = talloc_memdup(image, cert_table, image->sigsize); } @@ -239,11 +244,6 @@ static int image_pecoff_parse(struct image *image) return 0; } -static int align_up(int size, int align) -{ - return (size + align - 1) & ~(align - 1); -} - static int cmp_regions(const void *p1, const void *p2) { const struct region *r1 = p1, *r2 = p2; @@ -482,48 +482,106 @@ int image_hash_sha256(struct image *image, uint8_t digest[]) int image_add_signature(struct image *image, void *sig, int size) { - /* we only support one signature at present */ + struct cert_table_header *cth; + int tot_size = size + sizeof(*cth); + int aligned_size = align_up(tot_size, 8); + void *start; + if (image->sigbuf) { - fprintf(stderr, "warning: overwriting existing signature\n"); - talloc_free(image->sigbuf); + fprintf(stderr, "Image was already signed; adding additional signature\n"); + image->sigbuf = talloc_realloc(image, image->sigbuf, uint8_t, + image->sigsize + aligned_size); + start = image->sigbuf + image->sigsize; + image->sigsize += aligned_size; + } else { + fprintf(stderr, "Signing Unsigned original image\n"); + start = image->sigbuf = talloc_array(image, uint8_t, aligned_size); + image->sigsize = aligned_size; } - image->sigbuf = sig; - image->sigsize = size; + cth = start; + start += sizeof(*cth); + memset(cth, 0 , sizeof(*cth)); + cth->size = tot_size; + cth->revision = CERT_TABLE_REVISION; + cth->type = CERT_TABLE_TYPE_PKCS; + memcpy(start, sig, size); + if (aligned_size != tot_size) + memset(start + size, 0, aligned_size - tot_size); + return 0; } -void image_remove_signature(struct image *image) +int image_get_signature(struct image *image, int signum, + uint8_t **buf, size_t *size) { - if (image->sigbuf) - talloc_free(image->sigbuf); - image->sigbuf = NULL; - image->sigsize = 0; + struct cert_table_header *header; + void *addr = (void *)image->sigbuf; + int i; + + if (!image->sigbuf) { + fprintf(stderr, "No signature table present\n"); + return -1; + } + + header = addr; + for (i = 0; i < signum; i++) { + addr += align_up(header->size, 8); + header = addr; + } + if (addr >= ((void *)image->sigbuf + + image->sigsize)) + return -1; + + *buf = (void *)(header + 1); + *size = header->size - sizeof(*header); + return 0; +} + +int image_remove_signature(struct image *image, int signum) +{ + uint8_t *buf; + size_t size, aligned_size; + int rc = image_get_signature(image, signum, &buf, &size); + + if (rc) + return rc; + + buf -= sizeof(struct cert_table_header); + size += sizeof(struct cert_table_header); + aligned_size = align_up(size, 8); + + /* is signature at the end? */ + if (buf + aligned_size >= (uint8_t *)image->sigbuf + image->sigsize) { + /* only one signature? */ + if (image->sigbuf == buf) { + talloc_free(image->sigbuf); + image->sigbuf = NULL; + image->sigsize = 0; + return 0; + } + } else { + /* sig is in the middle ... just copy the rest over it */ + memmove(buf, buf + aligned_size, image->sigsize - + ((void *)buf - image->sigbuf) - aligned_size); + } + image->sigsize -= aligned_size; + image->sigbuf = talloc_realloc(image, image->sigbuf, uint8_t, + image->sigsize); + return 0; + } int image_write(struct image *image, const char *filename) { - struct cert_table_header cert_table_header; - int fd, rc, len, padlen; + int fd, rc; bool is_signed; - uint8_t pad[8]; is_signed = image->sigbuf && image->sigsize; - padlen = 0; /* optionally update the image to contain signature data */ if (is_signed) { - cert_table_header.size = image->sigsize + - sizeof(cert_table_header); - cert_table_header.revision = CERT_TABLE_REVISION; - cert_table_header.type = CERT_TABLE_TYPE_PKCS; - - len = sizeof(cert_table_header) + image->sigsize; - - /* pad to sizeof(pad)-byte boundary */ - padlen = align_up(len, sizeof(pad)) - len; - image->data_dir_sigtable->addr = image->data_size; - image->data_dir_sigtable->size = len + padlen; + image->data_dir_sigtable->size = image->sigsize; } else { image->data_dir_sigtable->addr = 0; image->data_dir_sigtable->size = 0; @@ -541,25 +599,24 @@ int image_write(struct image *image, const char *filename) if (!is_signed) goto out; - rc = write_all(fd, &cert_table_header, sizeof(cert_table_header)); - if (!rc) - goto out; - rc = write_all(fd, image->sigbuf, image->sigsize); if (!rc) goto out; - if (padlen) { - memset(pad, 0, sizeof(pad)); - rc = write_all(fd, pad, padlen); - } - out: close(fd); return !rc; } -int image_write_detached(struct image *image, const char *filename) +int image_write_detached(struct image *image, int signum, const char *filename) { - return fileio_write_file(filename, image->sigbuf, image->sigsize); + uint8_t *sig; + size_t len; + int rc; + + rc = image_get_signature(image, signum, &sig, &len); + + if (rc) + return rc; + return fileio_write_file(filename, sig, len); } diff --git a/src/image.h b/src/image.h index d68d002..37d1925 100644 --- a/src/image.h +++ b/src/image.h @@ -107,9 +107,11 @@ struct image *image_load(const char *filename); int image_hash_sha256(struct image *image, uint8_t digest[]); int image_add_signature(struct image *, void *sig, int size); -void image_remove_signature(struct image *image); +int image_get_signature(struct image *image, int signum, + uint8_t **buf, size_t *size); +int image_remove_signature(struct image *image, int signum); int image_write(struct image *image, const char *filename); -int image_write_detached(struct image *image, const char *filename); +int image_write_detached(struct image *image, int signum, const char *filename); #endif /* IMAGE_H */ diff --git a/src/sbattach.c b/src/sbattach.c index 012a422..dd03faf 100644 --- a/src/sbattach.c +++ b/src/sbattach.c @@ -64,6 +64,7 @@ static struct option options[] = { { "remove", no_argument, NULL, 'r' }, { "help", no_argument, NULL, 'h' }, { "version", no_argument, NULL, 'V' }, + { "signum", required_argument, NULL, 's' }, { NULL, 0, NULL, 0 }, }; @@ -80,7 +81,9 @@ static void usage(void) "\t--detach copy the boot image's signature table\n" "\t to \n" "\t--remove remove the boot image's signature\n" - "\t table from the original file\n", + "\t table from the original file\n" + "\t--signum signature to operate on (defaults to\n" + "\t first)\n", toolname, toolname, toolname); } @@ -89,9 +92,9 @@ static void version(void) printf("%s %s\n", toolname, VERSION); } -static int detach_sig(struct image *image, const char *sig_filename) +static int detach_sig(struct image *image, int signum, const char *sig_filename) { - return image_write_detached(image, sig_filename); + return image_write_detached(image, signum, sig_filename); } static int attach_sig(struct image *image, const char *image_filename, @@ -137,11 +140,18 @@ out: return rc; } -static int remove_sig(struct image *image, const char *image_filename) +static int remove_sig(struct image *image, int signum, + const char *image_filename) { int rc; - image_remove_signature(image); + rc = image_remove_signature(image, signum); + + if (rc) { + fprintf(stderr, "Error, image has no signature at %d\n", + signum + 1); + return rc; + } rc = image_write(image, image_filename); if (rc) @@ -163,7 +173,7 @@ int main(int argc, char **argv) struct image *image; enum action action; bool remove; - int c, rc; + int c, rc, signum = 0; action = ACTION_NONE; sig_filename = NULL; @@ -171,7 +181,7 @@ int main(int argc, char **argv) for (;;) { int idx; - c = getopt_long(argc, argv, "a:d:rhV", options, &idx); + c = getopt_long(argc, argv, "a:d:s:rhV", options, &idx); if (c == -1) break; @@ -186,6 +196,10 @@ int main(int argc, char **argv) action = (c == 'a') ? ACTION_ATTACH : ACTION_DETACH; sig_filename = optarg; break; + case 's': + /* humans count from 1 not zero */ + signum = atoi(optarg) - 1; + break; case 'r': remove = true; break; @@ -236,13 +250,13 @@ int main(int argc, char **argv) rc = attach_sig(image, image_filename, sig_filename); else if (action == ACTION_DETACH) - rc = detach_sig(image, sig_filename); + rc = detach_sig(image, signum, sig_filename); if (rc) goto out; if (remove) - rc = remove_sig(image, image_filename); + rc = remove_sig(image, signum, image_filename); out: talloc_free(image); diff --git a/src/sbsign.c b/src/sbsign.c index 58c6894..b5d2aaa 100644 --- a/src/sbsign.c +++ b/src/sbsign.c @@ -223,9 +223,15 @@ int main(int argc, char **argv) image_add_signature(ctx->image, buf, sigsize); - if (ctx->detached) - image_write_detached(ctx->image, ctx->outfilename); - else + if (ctx->detached) { + int i; + uint8_t *buf; + size_t len; + + for (i = 0; !image_get_signature(ctx->image, i, &buf, &len); i++) + ; + image_write_detached(ctx->image, i - 1, ctx->outfilename); + } else image_write(ctx->image, ctx->outfilename); talloc_free(ctx); diff --git a/src/sbverify.c b/src/sbverify.c index 4c4b2c6..84b300d 100644 --- a/src/sbverify.c +++ b/src/sbverify.c @@ -65,7 +65,7 @@ enum verify_status { static struct option options[] = { { "cert", required_argument, NULL, 'c' }, - { "no-verify", no_argument, NULL, 'n' }, + { "list", no_argument, NULL, 'l' }, { "detached", required_argument, NULL, 'd' }, { "verbose", no_argument, NULL, 'v' }, { "help", no_argument, NULL, 'h' }, @@ -79,7 +79,7 @@ static void usage(void) "Verify a UEFI secure boot image.\n\n" "Options:\n" "\t--cert certificate (x509 certificate)\n" - "\t--no-verify don't perform certificate verification\n" + "\t--list list all signatures (but don't verify)\n" "\t--detached read signature from , instead of\n" "\t looking for an embedded signature\n", toolname); @@ -157,23 +157,6 @@ static void print_certificate_store_certs(X509_STORE *certs) } } -static int load_image_signature_data(struct image *image, - uint8_t **buf, size_t *len) -{ - struct cert_table_header *header; - - if (!image->data_dir_sigtable->addr - || !image->data_dir_sigtable->size) { - fprintf(stderr, "No signature table present\n"); - return -1; - } - - header = (void *)image->buf + image->data_dir_sigtable->addr; - *buf = (void *)(header + 1); - *len = header->size - sizeof(*header); - return 0; -} - static int load_detached_signature_data(struct image *image, const char *filename, uint8_t **buf, size_t *len) { @@ -217,7 +200,7 @@ int main(int argc, char **argv) { const char *detached_sig_filename, *image_filename; enum verify_status status; - int rc, c, flags, verify; + int rc, c, flags, list; const uint8_t *tmp_buf; struct image *image; X509_STORE *certs; @@ -227,10 +210,11 @@ int main(int argc, char **argv) bool verbose; BIO *idcbio; PKCS7 *p7; + int sig_count = 0; status = VERIFY_FAIL; certs = X509_STORE_new(); - verify = 1; + list = 0; verbose = false; detached_sig_filename = NULL; @@ -244,7 +228,7 @@ int main(int argc, char **argv) for (;;) { int idx; - c = getopt_long(argc, argv, "c:d:nvVh", options, &idx); + c = getopt_long(argc, argv, "c:d:lvVh", options, &idx); if (c == -1) break; @@ -257,8 +241,8 @@ int main(int argc, char **argv) case 'd': detached_sig_filename = optarg; break; - case 'n': - verify = 0; + case 'l': + list = 1; break; case 'v': verbose = true; @@ -286,56 +270,76 @@ int main(int argc, char **argv) return EXIT_FAILURE; } - if (detached_sig_filename) - rc = load_detached_signature_data(image, detached_sig_filename, - &sig_buf, &sig_size); - else - rc = load_image_signature_data(image, &sig_buf, &sig_size); + for (;;) { + if (detached_sig_filename) { + if (sig_count++) + break; + + rc = load_detached_signature_data(image, detached_sig_filename, + &sig_buf, &sig_size); + } else + rc = image_get_signature(image, sig_count++, &sig_buf, &sig_size); + + if (rc) { + if (sig_count == 0) { + fprintf(stderr, "Unable to read signature data from %s\n", + detached_sig_filename ? : image_filename); + } + break; + } + + tmp_buf = sig_buf; + if (verbose || list) + printf("signature %d\n", sig_count); + p7 = d2i_PKCS7(NULL, &tmp_buf, sig_size); + if (!p7) { + fprintf(stderr, "Unable to parse signature data\n"); + ERR_print_errors_fp(stderr); + break; + } + + if (verbose || list) { + print_signature_info(p7); + //print_certificate_store_certs(certs); + } + + if (list) + continue; + + idcbio = BIO_new(BIO_s_mem()); + idc = IDC_get(p7, idcbio); + if (!idc) { + fprintf(stderr, "Unable to get IDC from PKCS7\n"); + break; + } + + rc = IDC_check_hash(idc, image); + if (rc) { + fprintf(stderr, "Image fails hash check\n"); + break; + } + + flags = PKCS7_BINARY; + + X509_STORE_set_verify_cb_func(certs, x509_verify_cb); + rc = PKCS7_verify(p7, NULL, certs, idcbio, NULL, flags); + if (rc) { + if (verbose) + printf("PKCS7 verification passed\n"); + + status = VERIFY_OK; + } else if (verbose) { + printf("PKCS7 verification failed\n"); + ERR_print_errors_fp(stderr); + } - if (rc) { - fprintf(stderr, "Unable to read signature data from %s\n", - detached_sig_filename ? : image_filename); - goto out; } - tmp_buf = sig_buf; - p7 = d2i_PKCS7(NULL, &tmp_buf, sig_size); - if (!p7) { - fprintf(stderr, "Unable to parse signature data\n"); - ERR_print_errors_fp(stderr); - goto out; - } - - if (verbose) { - print_signature_info(p7); - print_certificate_store_certs(certs); - } - - idcbio = BIO_new(BIO_s_mem()); - idc = IDC_get(p7, idcbio); - if (!idc) - goto out; - - rc = IDC_check_hash(idc, image); - if (rc) - goto out; - - flags = PKCS7_BINARY; - if (!verify) - flags |= PKCS7_NOVERIFY; - - X509_STORE_set_verify_cb_func(certs, x509_verify_cb); - rc = PKCS7_verify(p7, NULL, certs, idcbio, NULL, flags); - if (!rc) { - printf("PKCS7 verification failed\n"); - ERR_print_errors_fp(stderr); - goto out; - } - - status = VERIFY_OK; - -out: talloc_free(image); + + if (list) + exit(EXIT_SUCCESS); + if (status == VERIFY_OK) printf("Signature verification OK\n"); else