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 <JBottomley@Parallels.com>
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 @@
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 @@
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 @@
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_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 @@
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 @@
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 @@
{ "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 @@
"\t--detach <sigfile> copy the boot image's signature table\n"
"\t to <sigfile>\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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
"Verify a UEFI secure boot image.\n\n"
"Options:\n"
"\t--cert <certfile> 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 <file> read signature from <file>, instead of\n"
"\t looking for an embedded signature\n",
toolname);
@@ -157,23 +157,6 @@
}
}
-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 @@
{
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 @@
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 @@
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 @@
case 'd':
detached_sig_filename = optarg;
break;
- case 'n':
- verify = 0;
+ case 'l':
+ list = 1;
break;
case 'v':
verbose = true;
@@ -286,56 +270,76 @@
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;
- if (rc) {
- fprintf(stderr, "Unable to read signature data from %s\n",
- detached_sig_filename ? : image_filename);
- goto out;
+ 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);
+ }
+
}
- 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