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>
This commit is contained in:
James Bottomley 2014-12-19 16:10:34 -08:00
parent 6b493361c2
commit f6115a8045
5 changed files with 211 additions and 128 deletions

View file

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

View file

@ -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 */

View file

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

View file

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

View file

@ -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 <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 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