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:
parent
6b493361c2
commit
f6115a8045
5 changed files with 211 additions and 128 deletions
145
src/image.c
145
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);
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
12
src/sbsign.c
12
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);
|
||||
|
|
144
src/sbverify.c
144
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 <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
|
||||
|
|
Loading…
Reference in a new issue