Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
SUSE:SLE-12-SP4:GA
pesign
pesign-enable-supplementary-programs.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File pesign-enable-supplementary-programs.patch of Package pesign
From 4d80fec4a38b5cb1a63262a323353c23b0172b77 Mon Sep 17 00:00:00 2001 From: Gary Ching-Pang Lin <glin@suse.com> Date: Tue, 24 Dec 2013 11:33:26 +0800 Subject: [PATCH 01/31] Allocate cms_context for peverify_context This avoids the crash while freeing cms_context. Signed-off-by: Gary Ching-Pang Lin <glin@suse.com> --- src/peverify.c | 6 +++--- src/peverify_context.c | 4 ++-- src/peverify_context.h | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/peverify.c b/src/peverify.c index fc6de05..62e9995 100644 --- a/src/peverify.c +++ b/src/peverify.c @@ -55,8 +55,8 @@ open_input(peverify_context *ctx) exit(1); } - int rc = parse_signatures(&ctx->cms_ctx.signatures, - &ctx->cms_ctx.num_signatures, + int rc = parse_signatures(&ctx->cms_ctx->signatures, + &ctx->cms_ctx->num_signatures, ctx->inpe); if (rc < 0) { fprintf(stderr, "pesign: could not parse signature list in " @@ -99,7 +99,7 @@ check_signature(peverify_context *ctx) cert_iter iter; - generate_digest(&ctx->cms_ctx, ctx->inpe, 1); + generate_digest(ctx->cms_ctx, ctx->inpe, 1); if (check_db_hash(DBX, ctx) == FOUND) return -1; diff --git a/src/peverify_context.c b/src/peverify_context.c index 2e59f74..44fc442 100644 --- a/src/peverify_context.c +++ b/src/peverify_context.c @@ -54,7 +54,7 @@ peverify_context_init(peverify_context *ctx) ctx->infd = -1; - int rc = cms_context_init(&ctx->cms_ctx); + int rc = cms_context_alloc(&ctx->cms_ctx); if (rc < 0) return rc; @@ -67,7 +67,7 @@ peverify_context_fini(peverify_context *ctx) if (!ctx) return; - cms_context_fini(&ctx->cms_ctx); + cms_context_fini(ctx->cms_ctx); xfree(ctx->infile); diff --git a/src/peverify_context.h b/src/peverify_context.h index f9b0083..8599357 100644 --- a/src/peverify_context.h +++ b/src/peverify_context.h @@ -57,7 +57,7 @@ typedef struct peverify_context { dblist *db; dblist *dbx; - cms_context cms_ctx; + cms_context *cms_ctx; } peverify_context; extern int peverify_context_new(peverify_context **ctx); -- 1.8.4.5 From b6e40af634aa0b10f59b5936727ccfc260f3dcf0 Mon Sep 17 00:00:00 2001 From: Gary Ching-Pang Lin <glin@suse.com> Date: Tue, 24 Dec 2013 11:48:08 +0800 Subject: [PATCH 02/31] Calculate the dbsize to avoid the infinite loop Signed-off-by: Gary Ching-Pang Lin <glin@suse.com> --- src/certdb.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/certdb.c b/src/certdb.c index 5ef3ffe..b6e7c20 100644 --- a/src/certdb.c +++ b/src/certdb.c @@ -144,6 +144,10 @@ check_db(db_specifier which, peverify_context *ctx, checkfn check) if (found == FOUND) return FOUND; } + + dbsize -= certlist->SignatureListSize; + certlist = (EFI_SIGNATURE_LIST *)((uint8_t *)certlist + + certlist->SignatureListSize); } dbl = dbl->next; } -- 1.8.4.5 From cab9f9ff4737be3e3607caa6dd7f945c50fe64fa Mon Sep 17 00:00:00 2001 From: Gary Ching-Pang Lin <glin@suse.com> Date: Tue, 24 Dec 2013 12:35:02 +0800 Subject: [PATCH 03/31] Update the pathes of db, MokListRT, and dbx Signed-off-by: Gary Ching-Pang Lin <glin@suse.com> --- src/certdb.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/certdb.c b/src/certdb.c index b6e7c20..f6f52bc 100644 --- a/src/certdb.c +++ b/src/certdb.c @@ -75,9 +75,9 @@ add_cert_dbx(peverify_context *ctx, const char *filename) return add_db_file(ctx, DBX, filename); } -#define DB_PATH "/sys/firmware/efi/vars/db-d719b2cb-3d3a-4596-a3bc-dad00e67656f/data" -#define MOK_PATH "/sys/firmware/efi/vars/fixmefixmefixme/data" -#define DBX_PATH "/sys/firmware/efi/vars/dbx-d719b2cb-3d3a-4596-a3bc-dad00e67656f/data" +#define DB_PATH "/sys/firmware/efi/efivars/db-d719b2cb-3d3a-4596-a3bc-dad00e67656f" +#define MOK_PATH "/sys/firmware/efi/efivars/MokListRT-605dab50-e046-4300-abb6-3dd810dd8b23" +#define DBX_PATH "/sys/firmware/efi/efivars/dbx-d719b2cb-3d3a-4596-a3bc-dad00e67656f" void init_cert_db(peverify_context *ctx, int use_system_dbs) @@ -97,7 +97,7 @@ init_cert_db(peverify_context *ctx, int use_system_dbs) rc = add_db_file(ctx, DB, MOK_PATH); if (rc < 0 && errno != ENOENT) { fprintf(stderr, "peverify: Could not add key database " - "\"%s\": %m\n", DB_PATH); + "\"%s\": %m\n", MOK_PATH); exit(1); } -- 1.8.4.5 From 200bff332ee34de2e2679cfdddd8d09a78b536f7 Mon Sep 17 00:00:00 2001 From: Gary Ching-Pang Lin <glin@suse.com> Date: Tue, 24 Dec 2013 14:53:58 +0800 Subject: [PATCH 04/31] Skip the first 4 bytes in the efi variables The first 4 bytes store the attributes of the efi variable. Signed-off-by: Gary Ching-Pang Lin <glin@suse.com> --- src/certdb.c | 25 +++++++++++++++++-------- src/peverify_context.h | 2 ++ 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/certdb.c b/src/certdb.c index f6f52bc..d9d4dea 100644 --- a/src/certdb.c +++ b/src/certdb.c @@ -26,7 +26,7 @@ #include "peverify.h" static int -add_db_file(peverify_context *ctx, db_specifier which, const char *dbfile) +add_db_file(peverify_context *ctx, db_specifier which, const char *dbfile, int efivar) { dblist *db = calloc(1, sizeof (dblist)); @@ -55,6 +55,15 @@ add_db_file(peverify_context *ctx, db_specifier which, const char *dbfile) return -1; } + /* skip the first 4 bytes (EFI attributes) in the efi variable */ + if (efivar == 1) { + db->data = db->map + 4; + db->datalen = db->size - 4; + } else { + db->data = db->map; + db->datalen = db->size; + } + dblist **tmp = which == DB ? &ctx->db : &ctx->dbx; db->next = *tmp; @@ -66,13 +75,13 @@ add_db_file(peverify_context *ctx, db_specifier which, const char *dbfile) int add_cert_db(peverify_context *ctx, const char *filename) { - return add_db_file(ctx, DB, filename); + return add_db_file(ctx, DB, filename, 0); } int add_cert_dbx(peverify_context *ctx, const char *filename) { - return add_db_file(ctx, DBX, filename); + return add_db_file(ctx, DBX, filename, 0); } #define DB_PATH "/sys/firmware/efi/efivars/db-d719b2cb-3d3a-4596-a3bc-dad00e67656f" @@ -87,14 +96,14 @@ init_cert_db(peverify_context *ctx, int use_system_dbs) if (!use_system_dbs) return; - rc = add_db_file(ctx, DB, DB_PATH); + rc = add_db_file(ctx, DB, DB_PATH, 1); if (rc < 0 && errno != ENOENT) { fprintf(stderr, "peverify: Could not add key database " "\"%s\": %m\n", DB_PATH); exit(1); } - rc = add_db_file(ctx, DB, MOK_PATH); + rc = add_db_file(ctx, DB, MOK_PATH, 1); if (rc < 0 && errno != ENOENT) { fprintf(stderr, "peverify: Could not add key database " "\"%s\": %m\n", MOK_PATH); @@ -106,7 +115,7 @@ init_cert_db(peverify_context *ctx, int use_system_dbs) "No key database available\n"); } - rc = add_db_file(ctx, DBX, DBX_PATH); + rc = add_db_file(ctx, DBX, DBX_PATH, 1); if (rc < 0 && errno != ENOENT) { fprintf(stderr, "peverify: Could not add revocation " "database \"%s\": %m\n", DBX_PATH); @@ -126,10 +135,10 @@ check_db(db_specifier which, peverify_context *ctx, checkfn check) while (dbl) { EFI_SIGNATURE_LIST *certlist; EFI_SIGNATURE_DATA *cert; - size_t dbsize = dbl->size; + size_t dbsize = dbl->datalen; unsigned long certcount; - certlist = dbl->map; + certlist = dbl->data; while (dbsize > 0 && dbsize >= certlist->SignatureListSize) { certcount = (certlist->SignatureListSize - certlist->SignatureHeaderSize) diff --git a/src/peverify_context.h b/src/peverify_context.h index 8599357..37f415b 100644 --- a/src/peverify_context.h +++ b/src/peverify_context.h @@ -31,6 +31,8 @@ struct dblist { struct dblist *next; size_t size; void *map; + size_t datalen; + void *data; }; typedef struct dblist dblist; -- 1.8.4.5 From 237e983fe11800e36074c2a50d6468b7ac45ef12 Mon Sep 17 00:00:00 2001 From: Gary Ching-Pang Lin <glin@suse.com> Date: Wed, 25 Dec 2013 14:14:48 +0800 Subject: [PATCH 05/31] Match the hashes in the db list Signed-off-by: Gary Ching-Pang Lin <glin@suse.com> --- src/certdb.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/certdb.c b/src/certdb.c index d9d4dea..470f7f3 100644 --- a/src/certdb.c +++ b/src/certdb.c @@ -144,7 +144,8 @@ check_db(db_specifier which, peverify_context *ctx, checkfn check) certlist->SignatureHeaderSize) / certlist->SignatureSize; cert = (EFI_SIGNATURE_DATA *)((uint8_t *)certlist + - sizeof(*cert) + certlist->SignatureHeaderSize); + sizeof(EFI_SIGNATURE_LIST) + + certlist->SignatureHeaderSize); for (int i = 0; i < certcount; i++) { found = check(ctx, @@ -152,6 +153,8 @@ check_db(db_specifier which, peverify_context *ctx, checkfn check) &certlist->SignatureType); if (found == FOUND) return FOUND; + cert = (EFI_SIGNATURE_DATA *)((uint8_t *)cert + + certlist->SignatureSize); } dbsize -= certlist->SignatureListSize; @@ -166,6 +169,20 @@ check_db(db_specifier which, peverify_context *ctx, checkfn check) static db_status check_hash(peverify_context *ctx, void *sigdata, efi_guid_t *sigtype) { + efi_guid_t efi_sha256 = EFI_CERT_SHA256_GUID; + efi_guid_t efi_sha1 = EFI_CERT_SHA1_GUID; + void *digest; + + if (memcmp(sigtype, &efi_sha256, sizeof(efi_guid_t)) == 0) { + digest = ctx->cms_ctx->digests[0].pe_digest->data; + if (memcmp (digest, sigdata, 32) == 0) + return FOUND; + } else if (memcmp(sigtype, &efi_sha1, sizeof(efi_guid_t)) == 0) { + digest = ctx->cms_ctx->digests[1].pe_digest->data; + if (memcmp (digest, sigdata, 20) == 0) + return FOUND; + } + return NOT_FOUND; } -- 1.8.4.5 From 135a083d0e648255096128a67463bc2191f4ac4a Mon Sep 17 00:00:00 2001 From: Gary Ching-Pang Lin <glin@suse.com> Date: Tue, 24 Dec 2013 11:47:14 +0800 Subject: [PATCH 06/31] Verify the signature with the certs in the dblist Signed-off-by: Gary Ching-Pang Lin <glin@suse.com> --- src/certdb.c | 127 +++++++++++++++++++++++++++++++++++++++++++++++++++------ src/peverify.c | 66 +++++++++++++++++++++++++++++- 2 files changed, 179 insertions(+), 14 deletions(-) diff --git a/src/certdb.c b/src/certdb.c index 470f7f3..4937c44 100644 --- a/src/certdb.c +++ b/src/certdb.c @@ -23,6 +23,12 @@ #include <sys/stat.h> #include <unistd.h> +#include <nss.h> +#include <prerror.h> +#include <cert.h> +#include <pkcs7t.h> +#include <pk11pub.h> + #include "peverify.h" static int @@ -123,15 +129,23 @@ init_cert_db(peverify_context *ctx, int use_system_dbs) } } -typedef db_status (*checkfn)(peverify_context *ctx, void *sigdata, - efi_guid_t *sigtype); +typedef db_status (*checkfn)(peverify_context *ctx, SECItem *sig, + efi_guid_t *sigtype, SECItem *pkcs7sig); static db_status -check_db(db_specifier which, peverify_context *ctx, checkfn check) +check_db(db_specifier which, peverify_context *ctx, checkfn check, + void *data, ssize_t datalen) { + SECItem pkcs7sig, sig; dblist *dbl = which == DB ? ctx->db : ctx->dbx; db_status found = NOT_FOUND; + pkcs7sig.data = data; + pkcs7sig.len = datalen; + pkcs7sig.type = siBuffer; + + sig.type = siBuffer; + while (dbl) { EFI_SIGNATURE_LIST *certlist; EFI_SIGNATURE_DATA *cert; @@ -148,9 +162,10 @@ check_db(db_specifier which, peverify_context *ctx, checkfn check) certlist->SignatureHeaderSize); for (int i = 0; i < certcount; i++) { - found = check(ctx, - cert->SignatureData, - &certlist->SignatureType); + sig.data = cert->SignatureData; + sig.len = certlist->SignatureSize - sizeof(efi_guid_t); + found = check(ctx, &sig, &certlist->SignatureType, + &pkcs7sig); if (found == FOUND) return FOUND; cert = (EFI_SIGNATURE_DATA *)((uint8_t *)cert + @@ -167,7 +182,8 @@ check_db(db_specifier which, peverify_context *ctx, checkfn check) } static db_status -check_hash(peverify_context *ctx, void *sigdata, efi_guid_t *sigtype) +check_hash(peverify_context *ctx, SECItem *sig, efi_guid_t *sigtype, + SECItem *pkcs7sig) { efi_guid_t efi_sha256 = EFI_CERT_SHA256_GUID; efi_guid_t efi_sha1 = EFI_CERT_SHA1_GUID; @@ -175,11 +191,11 @@ check_hash(peverify_context *ctx, void *sigdata, efi_guid_t *sigtype) if (memcmp(sigtype, &efi_sha256, sizeof(efi_guid_t)) == 0) { digest = ctx->cms_ctx->digests[0].pe_digest->data; - if (memcmp (digest, sigdata, 32) == 0) + if (memcmp (digest, sig->data, 32) == 0) return FOUND; } else if (memcmp(sigtype, &efi_sha1, sizeof(efi_guid_t)) == 0) { digest = ctx->cms_ctx->digests[1].pe_digest->data; - if (memcmp (digest, sigdata, 20) == 0) + if (memcmp (digest, sig->data, 20) == 0) return FOUND; } @@ -189,17 +205,102 @@ check_hash(peverify_context *ctx, void *sigdata, efi_guid_t *sigtype) db_status check_db_hash(db_specifier which, peverify_context *ctx) { - return check_db(which, ctx, check_hash); + return check_db(which, ctx, check_hash, NULL, 0); } static db_status -check_cert(peverify_context *ctx, void *sigdata, efi_guid_t *sigtype) +check_cert(peverify_context *ctx, SECItem *sig, efi_guid_t *sigtype, + SECItem *pkcs7sig) { - return NOT_FOUND; + SEC_PKCS7ContentInfo *cinfo = NULL; + CERTCertificate *cert = NULL; + CERTCertTrust trust; + SECItem *content, *digest = NULL; + PK11Context *pk11ctx = NULL; + SECOidData *oid; + PRBool result; + SECStatus rv; + db_status status = NOT_FOUND; + + efi_guid_t efi_x509 = EFI_CERT_X509_GUID; + + if (memcmp(sigtype, &efi_x509, sizeof(efi_guid_t)) != 0) + return NOT_FOUND; + + cinfo = SEC_PKCS7DecodeItem(pkcs7sig, NULL, NULL, NULL, NULL, NULL, + NULL, NULL); + if (!cinfo) + goto out; + + /* Generate the digest of contentInfo */ + /* XXX support only sha256 for now */ + digest = SECITEM_AllocItem(NULL, NULL, 32); + if (digest == NULL) + goto out; + + content = cinfo->content.signedData->contentInfo.content.data; + oid = SECOID_FindOIDByTag(SEC_OID_SHA256); + if (oid == NULL) + goto out; + pk11ctx = PK11_CreateDigestContext(oid->offset); + if (ctx == NULL) + goto out; + if (PK11_DigestBegin(pk11ctx) != SECSuccess) + goto out; + /* Skip the SEQUENCE tag */ + if (PK11_DigestOp(pk11ctx, content->data + 2, content->len - 2) != SECSuccess) + goto out; + if (PK11_DigestFinal(pk11ctx, digest->data, &digest->len, 32) != SECSuccess) + goto out; + + /* Import the trusted certificate */ + cert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), sig, "Temp CA", + PR_FALSE, PR_TRUE); + if (!cert) { + fprintf(stderr, "Unable to create cert: %s\n", + PORT_ErrorToString(PORT_GetError())); + goto out; + } + + rv = CERT_DecodeTrustString(&trust, ",,P"); + if (rv != SECSuccess) { + fprintf(stderr, "Unable to decode trust string: %s\n", + PORT_ErrorToString(PORT_GetError())); + goto out; + } + + rv = CERT_ChangeCertTrust(CERT_GetDefaultCertDB(), cert, &trust); + if (rv != SECSuccess) { + fprintf(stderr, "Failed to change cert trust: %s\n", + PORT_ErrorToString(PORT_GetError())); + goto out; + } + + /* Verify the signature */ + result = SEC_PKCS7VerifyDetachedSignature(cinfo, certUsageObjectSigner, + digest, HASH_AlgSHA256, + PR_FALSE); + if (!result) { + fprintf(stderr, "%s\n", PORT_ErrorToString(PORT_GetError())); + goto out; + } + + status = FOUND; +out: + if (cinfo) + SEC_PKCS7DestroyContentInfo(cinfo); + if (cert) + CERT_DestroyCertificate(cert); + if (pk11ctx) + PK11_DestroyContext(pk11ctx, PR_TRUE); + if (digest) + SECITEM_FreeItem(digest, PR_FALSE); + + return status; } db_status check_db_cert(db_specifier which, peverify_context *ctx, void *data, ssize_t datalen) { - return check_db(which, ctx, check_cert); + return check_db(which, ctx, check_cert, data, datalen); } diff --git a/src/peverify.c b/src/peverify.c index 62e9995..47d7ee1 100644 --- a/src/peverify.c +++ b/src/peverify.c @@ -24,11 +24,15 @@ #include <unistd.h> #include <sys/stat.h> #include <sys/types.h> +#include <ftw.h> +#include <nss.h> #include <popt.h> +#include <prerror.h> #include <cert.h> #include <pkcs7t.h> +#include <pk11pub.h> #include "peverify.h" @@ -87,7 +91,34 @@ check_inputs(peverify_context *ctx) static int cert_matches_digest(peverify_context *ctx, void *data, ssize_t datalen) { - return -1; + SECItem sig, *pe_digest, *content; + uint8_t *digest; + SEC_PKCS7ContentInfo *cinfo = NULL; + int ret = -1; + + sig.data = data; + sig.len = datalen; + sig.type = siBuffer; + + cinfo = SEC_PKCS7DecodeItem(&sig, NULL, NULL, NULL, NULL, NULL, + NULL, NULL); + + if (!SEC_PKCS7ContentIsSigned(cinfo)) + goto out; + + /* TODO Find out the digest type in spc_content */ + pe_digest = ctx->cms_ctx->digests[0].pe_digest; + content = cinfo->content.signedData->contentInfo.content.data; + digest = content->data + content->len - pe_digest->len; + if (memcmp(pe_digest->data, digest, pe_digest->len) != 0) + goto out; + + ret = 0; +out: + if (cinfo) + SEC_PKCS7DestroyContentInfo(cinfo); + + return ret; } static int @@ -164,6 +195,23 @@ callback(poptContext con, enum poptCallbackReason reason, } } +static int +delete_files(const char *fpath, const struct stat *sb, int typeflag) +{ + if (typeflag == FTW_F) + remove(fpath); + + return 0; +} + +static void +remove_certdir(const char *certdir) +{ + ftw(certdir, delete_files, 3); + + remove(certdir); +} + int main(int argc, char *argv[]) { @@ -175,6 +223,10 @@ main(int argc, char *argv[]) char *dbxfile = NULL; int use_system_dbs = 1; + char template[] = "/tmp/peverify-XXXXXX"; + char *certdir = NULL; + SECStatus status; + poptContext optCon; struct poptOption options[] = { {"dbfile", 'D', POPT_ARG_CALLBACK|POPT_CBFLAG_POST, (void *)callback, 0, (void *)ctxp, NULL }, @@ -226,6 +278,14 @@ main(int argc, char *argv[]) init_cert_db(ctxp, use_system_dbs); + certdir = mkdtemp(template); + status = NSS_InitReadWrite(certdir); + if (status != SECSuccess) { + fprintf(stderr, "Could not initialize nss: %s\n", + PORT_ErrorToString(PORT_GetError())); + exit(1); + } + rc = check_signature(ctxp); close_input(ctxp); @@ -233,5 +293,9 @@ main(int argc, char *argv[]) printf("peverify: \"%s\" is %s.\n", ctx.infile, rc >= 0 ? "valid" : "invalid"); peverify_context_fini(&ctx); + + NSS_Shutdown(); + remove_certdir(certdir); + return (rc < 0); } -- 1.8.4.5 From 35746653e0af5b129dfdfd33e9954ff5c47062aa Mon Sep 17 00:00:00 2001 From: Gary Ching-Pang Lin <glin@suse.com> Date: Fri, 27 Dec 2013 17:42:19 +0800 Subject: [PATCH 07/31] Verify the PE image with a certificate Signed-off-by: Gary Ching-Pang Lin <glin@suse.com> --- src/certdb.c | 57 ++++++++++++++++++++++++++++++++++++++++---------- src/certdb.h | 1 + src/peverify.c | 5 +++++ src/peverify_context.c | 4 ++++ src/peverify_context.h | 7 +++++++ 5 files changed, 63 insertions(+), 11 deletions(-) diff --git a/src/certdb.c b/src/certdb.c index 4937c44..922b783 100644 --- a/src/certdb.c +++ b/src/certdb.c @@ -22,6 +22,7 @@ #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> +#include <string.h> #include <nss.h> #include <prerror.h> @@ -32,13 +33,16 @@ #include "peverify.h" static int -add_db_file(peverify_context *ctx, db_specifier which, const char *dbfile, int efivar) +add_db_file(peverify_context *ctx, db_specifier which, const char *dbfile, + db_f_type type) { dblist *db = calloc(1, sizeof (dblist)); if (!db) return -1; + db->type = type; + db->fd = open(dbfile, O_RDONLY); if (db->fd < 0) { save_errno(free(db)); @@ -61,13 +65,38 @@ add_db_file(peverify_context *ctx, db_specifier which, const char *dbfile, int e return -1; } - /* skip the first 4 bytes (EFI attributes) in the efi variable */ - if (efivar == 1) { - db->data = db->map + 4; - db->datalen = db->size - 4; - } else { + EFI_SIGNATURE_LIST *certlist; + EFI_SIGNATURE_DATA *cert; + efi_guid_t efi_x509 = EFI_CERT_X509_GUID; + + switch (type) { + case DB_FILE: db->data = db->map; db->datalen = db->size; + break; + case DB_EFIVAR: + /* skip the first 4 bytes (EFI attributes) */ + db->data = db->map + 4; + db->datalen = db->size - 4; + break; + case DB_CERT: + db->datalen = db->size + sizeof(EFI_SIGNATURE_LIST) + + sizeof(efi_guid_t); + db->data = calloc(1, db->datalen); + if (!db->data) + return -1; + + certlist = (EFI_SIGNATURE_LIST *)db->data; + memcpy((void *)&certlist->SignatureType, &efi_x509, sizeof(efi_guid_t)); + certlist->SignatureListSize = db->datalen; + certlist->SignatureHeaderSize = 0; + certlist->SignatureSize = db->size + sizeof(efi_guid_t); + + cert = (EFI_SIGNATURE_DATA *)(db->data + sizeof(EFI_SIGNATURE_LIST)); + memcpy((void *)cert->SignatureData, db->map, db->size); + break; + default: + break; } dblist **tmp = which == DB ? &ctx->db : &ctx->dbx; @@ -81,13 +110,19 @@ add_db_file(peverify_context *ctx, db_specifier which, const char *dbfile, int e int add_cert_db(peverify_context *ctx, const char *filename) { - return add_db_file(ctx, DB, filename, 0); + return add_db_file(ctx, DB, filename, DB_FILE); } int add_cert_dbx(peverify_context *ctx, const char *filename) { - return add_db_file(ctx, DBX, filename, 0); + return add_db_file(ctx, DBX, filename, DB_FILE); +} + +int +add_cert_file(peverify_context *ctx, const char *filename) +{ + return add_db_file(ctx, DB, filename, DB_CERT); } #define DB_PATH "/sys/firmware/efi/efivars/db-d719b2cb-3d3a-4596-a3bc-dad00e67656f" @@ -102,14 +137,14 @@ init_cert_db(peverify_context *ctx, int use_system_dbs) if (!use_system_dbs) return; - rc = add_db_file(ctx, DB, DB_PATH, 1); + rc = add_db_file(ctx, DB, DB_PATH, DB_EFIVAR); if (rc < 0 && errno != ENOENT) { fprintf(stderr, "peverify: Could not add key database " "\"%s\": %m\n", DB_PATH); exit(1); } - rc = add_db_file(ctx, DB, MOK_PATH, 1); + rc = add_db_file(ctx, DB, MOK_PATH, DB_EFIVAR); if (rc < 0 && errno != ENOENT) { fprintf(stderr, "peverify: Could not add key database " "\"%s\": %m\n", MOK_PATH); @@ -121,7 +156,7 @@ init_cert_db(peverify_context *ctx, int use_system_dbs) "No key database available\n"); } - rc = add_db_file(ctx, DBX, DBX_PATH, 1); + rc = add_db_file(ctx, DBX, DBX_PATH, DB_EFIVAR); if (rc < 0 && errno != ENOENT) { fprintf(stderr, "peverify: Could not add revocation " "database \"%s\": %m\n", DBX_PATH); diff --git a/src/certdb.h b/src/certdb.h index ee70ba6..d64494d 100644 --- a/src/certdb.h +++ b/src/certdb.h @@ -48,5 +48,6 @@ extern db_status check_db_cert(db_specifier which, peverify_context *ctx, extern void init_cert_db(peverify_context *ctx, int use_system_dbs); extern int add_cert_db(peverify_context *ctx, const char *filename); extern int add_cert_dbx(peverify_context *ctx, const char *filename); +extern int add_cert_file(peverify_context *ctx, const char *filename); #endif /* CERTDB_H */ diff --git a/src/peverify.c b/src/peverify.c index 47d7ee1..e4c3e13 100644 --- a/src/peverify.c +++ b/src/peverify.c @@ -187,6 +187,8 @@ callback(poptContext con, enum poptCallbackReason reason, rc = add_cert_db(ctx, arg); } else if (opt->shortName == 'X') { rc = add_cert_dbx(ctx, arg); + } else if (opt->shortName == 'c') { + rc = add_cert_file(ctx, arg); } if (rc != 0) { fprintf(stderr, "Could not add %s from file \"%s\": %m\n", @@ -221,6 +223,7 @@ main(int argc, char *argv[]) char *dbfile = NULL; char *dbxfile = NULL; + char *certfile = NULL; int use_system_dbs = 1; char template[] = "/tmp/peverify-XXXXXX"; @@ -242,6 +245,8 @@ main(int argc, char *argv[]) "use file for allowed certificate list", "<dbfile>" }, {"dbxfile", 'X', POPT_ARG_STRING, &dbxfile, 0, "use file for disallowed certificate list","<dbxfile>"}, + {"certfile", 'c', POPT_ARG_STRING, &certfile, 0, + "the certificate (in DER form) for verification ","<certfile>"}, POPT_AUTOALIAS POPT_AUTOHELP POPT_TABLEEND diff --git a/src/peverify_context.c b/src/peverify_context.c index 44fc442..d3aa53e 100644 --- a/src/peverify_context.c +++ b/src/peverify_context.c @@ -82,6 +82,8 @@ peverify_context_fini(peverify_context *ctx) while (ctx->db) { dblist *db = ctx->db; + if (db->type == DB_CERT) + free(db->data); munmap(db->map, db->size); close(db->fd); ctx->db = db->next; @@ -90,6 +92,8 @@ peverify_context_fini(peverify_context *ctx) while (ctx->dbx) { dblist *db = ctx->dbx; + if (db->type == DB_CERT) + free(db->data); munmap(db->map, db->size); close(db->fd); ctx->dbx = db->next; diff --git a/src/peverify_context.h b/src/peverify_context.h index 37f415b..7e26d06 100644 --- a/src/peverify_context.h +++ b/src/peverify_context.h @@ -26,7 +26,14 @@ enum { PEVERIFY_C_ALLOCATED = 1, }; +typedef enum { + DB_FILE, + DB_EFIVAR, + DB_CERT, +} db_f_type; + struct dblist { + db_f_type type; int fd; struct dblist *next; size_t size; -- 1.8.4.5 From 23295225a732058edabc58ede7e863d347d2ac47 Mon Sep 17 00:00:00 2001 From: Gary Ching-Pang Lin <glin@suse.com> Date: Fri, 27 Dec 2013 17:43:32 +0800 Subject: [PATCH 08/31] It's peverify, not pesign :) Signed-off-by: Gary Ching-Pang Lin <glin@suse.com> --- src/peverify.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/peverify.c b/src/peverify.c index e4c3e13..ebd7ee7 100644 --- a/src/peverify.c +++ b/src/peverify.c @@ -40,21 +40,21 @@ static void open_input(peverify_context *ctx) { if (!ctx->infile) { - fprintf(stderr, "pesign: No input file specified.\n"); + fprintf(stderr, "peverify: No input file specified.\n"); exit(1); } ctx->infd = open(ctx->infile, O_RDONLY|O_CLOEXEC); if (ctx->infd < 0) { - fprintf(stderr, "pesign: Error opening input: %m\n"); + fprintf(stderr, "peverify: Error opening input: %m\n"); exit(1); } Pe_Cmd cmd = ctx->infd == STDIN_FILENO ? PE_C_READ : PE_C_READ_MMAP; ctx->inpe = pe_begin(ctx->infd, cmd, NULL); if (!ctx->inpe) { - fprintf(stderr, "pesign: could not load input file: %s\n", + fprintf(stderr, "peverify: could not load input file: %s\n", pe_errmsg(pe_errno())); exit(1); } @@ -63,7 +63,7 @@ open_input(peverify_context *ctx) &ctx->cms_ctx->num_signatures, ctx->inpe); if (rc < 0) { - fprintf(stderr, "pesign: could not parse signature list in " + fprintf(stderr, "peverify: could not parse signature list in " "EFI binary\n"); exit(1); } -- 1.8.4.5 From b431e22f0e02e282ece114e1829575e7eedfcfb5 Mon Sep 17 00:00:00 2001 From: Peter Jones <pjones@redhat.com> Date: Mon, 6 Jan 2014 14:11:34 -0500 Subject: [PATCH 09/31] Rename peverify to pesigcheck Signed-off-by: Peter Jones <pjones@redhat.com> --- src/.gitignore | 2 +- src/Makefile | 16 +-- src/certdb.c | 32 ++--- src/certdb.h | 12 +- src/pesigcheck.1 | 25 ++++ src/pesigcheck.c | 306 +++++++++++++++++++++++++++++++++++++++++++++++ src/pesigcheck.h | 39 ++++++ src/pesigcheck_context.c | 122 +++++++++++++++++++ src/pesigcheck_context.h | 78 ++++++++++++ src/peverify.1 | 25 ---- src/peverify.c | 306 ----------------------------------------------- src/peverify.h | 39 ------ src/peverify_context.c | 122 ------------------- src/peverify_context.h | 78 ------------ 14 files changed, 601 insertions(+), 601 deletions(-) create mode 100644 src/pesigcheck.1 create mode 100644 src/pesigcheck.c create mode 100644 src/pesigcheck.h create mode 100644 src/pesigcheck_context.c create mode 100644 src/pesigcheck_context.h delete mode 100644 src/peverify.1 delete mode 100644 src/peverify.c delete mode 100644 src/peverify.h delete mode 100644 src/peverify_context.c delete mode 100644 src/peverify_context.h diff --git a/src/.gitignore b/src/.gitignore index fec12ae..7215f64 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -12,4 +12,4 @@ ms client efikeygen efisiglist -peverify +pesigcheck diff --git a/src/Makefile b/src/Makefile index f478aa6..0aa13a1 100644 --- a/src/Makefile +++ b/src/Makefile @@ -10,7 +10,7 @@ LDFLAGS = CCLDFLAGS = -L../libdpe $(foreach pklib,$(PKLIBS), $(shell pkg-config --libs-only-L $(pklib))) CFLAGS += -I../include/ $(foreach pklib,$(PKLIBS), $(shell pkg-config --cflags $(pklib))) -Werror -TARGETS = pesign authvar client efisiglist efikeygen peverify +TARGETS = pesign authvar client efisiglist efikeygen pesigcheck all : $(TARGETS) @@ -29,10 +29,10 @@ pesign_OBJECTS = $(foreach source,$(pesign_SOURCES),$(patsubst %.c,%,$(source)). pesign_DEPS = $(foreach source,$(pesign_SOURCES),.$(patsubst %.c,%,$(source)).P) pesign : $(pesign_OBJECTS) $(STATIC_LIBS) -peverify_SOURCES = peverify.c peverify_context.c certdb.c -peverify_OBJECTS = $(foreach source,$(peverify_SOURCES),$(patsubst %.c,%,$(source)).o) generic.a -peverify_DEPS = $(foreach source,$(peverify_SOURCES),.$(patsubst %.c,%,$(source)).P) -peverify : $(peverify_OBJECTS) $(STATIC_LIBS) +pesigcheck_SOURCES = pesigcheck.c pesigcheck_context.c certdb.c +pesigcheck_OBJECTS = $(foreach source,$(pesigcheck_SOURCES),$(patsubst %.c,%,$(source)).o) generic.a +pesigcheck_DEPS = $(foreach source,$(pesigcheck_SOURCES),.$(patsubst %.c,%,$(source)).P) +pesigcheck : $(pesigcheck_OBJECTS) $(STATIC_LIBS) client_SOURCES = pesign_context.c actions.c client.c client_OBJECTS = $(foreach source,$(client_SOURCES),$(patsubst %.c,%,$(source)).o) generic.a @@ -55,7 +55,7 @@ fuzzsocket_DEPS = $(foreach source,$(fuzzsocket_SOURCES),.$(patsubst %.c,%,$(sou fuzzsocket : $(fuzzsocket_OBJECTS) -lrt DEPS = $(generic_DEPS) $(authvar_DEPS) $(pesign_DEPS) $(client_DEPS) \ - $(peverify_DEPS) $(efisiglist_DEPS) $(efikeygen_DEPS) + $(pesigcheck_DEPS) $(efisiglist_DEPS) $(efikeygen_DEPS) deps : $(DEPS) @@ -84,14 +84,14 @@ install : $(INSTALL) -m 755 pesign $(INSTALLROOT)$(PREFIX)/bin/ $(INSTALL) -m 755 client $(INSTALLROOT)$(PREFIX)/bin/pesign-client $(INSTALL) -m 755 efikeygen $(INSTALLROOT)$(PREFIX)/bin/ - #$(INSTALL) -m 755 peverify $(INSTALLROOT)$(PREFIX)/bin/ + #$(INSTALL) -m 755 pesigcheck $(INSTALLROOT)$(PREFIX)/bin/ $(INSTALL) -d -m 755 $(INSTALLROOT)/etc/popt.d/ $(INSTALL) -m 644 pesign.popt $(INSTALLROOT)/etc/popt.d/ $(INSTALL) -d -m 755 $(INSTALLROOT)/usr/share/man/man1/ $(INSTALL) -m 644 pesign.1 $(INSTALLROOT)/usr/share/man/man1/ $(INSTALL) -m 644 pesign-client.1 $(INSTALLROOT)/usr/share/man/man1/ $(INSTALL) -m 644 efikeygen.1 $(INSTALLROOT)/usr/share/man/man1/ - #$(INSTALL) -m 644 peverify.1 $(INSTALLROOT)/usr/share/man/man1/ + #$(INSTALL) -m 644 pesigcheck.1 $(INSTALLROOT)/usr/share/man/man1/ $(INSTALL) -d -m 755 $(INSTALLROOT)/etc/rpm/ $(INSTALL) -m 644 macros.pesign $(INSTALLROOT)/etc/rpm/ diff --git a/src/certdb.c b/src/certdb.c index 922b783..24c319b 100644 --- a/src/certdb.c +++ b/src/certdb.c @@ -30,10 +30,10 @@ #include <pkcs7t.h> #include <pk11pub.h> -#include "peverify.h" +#include "pesigcheck.h" static int -add_db_file(peverify_context *ctx, db_specifier which, const char *dbfile, +add_db_file(pesigcheck_context *ctx, db_specifier which, const char *dbfile, db_f_type type) { dblist *db = calloc(1, sizeof (dblist)); @@ -108,19 +108,19 @@ add_db_file(peverify_context *ctx, db_specifier which, const char *dbfile, } int -add_cert_db(peverify_context *ctx, const char *filename) +add_cert_db(pesigcheck_context *ctx, const char *filename) { return add_db_file(ctx, DB, filename, DB_FILE); } int -add_cert_dbx(peverify_context *ctx, const char *filename) +add_cert_dbx(pesigcheck_context *ctx, const char *filename) { return add_db_file(ctx, DBX, filename, DB_FILE); } int -add_cert_file(peverify_context *ctx, const char *filename) +add_cert_file(pesigcheck_context *ctx, const char *filename) { return add_db_file(ctx, DB, filename, DB_CERT); } @@ -130,7 +130,7 @@ add_cert_file(peverify_context *ctx, const char *filename) #define DBX_PATH "/sys/firmware/efi/efivars/dbx-d719b2cb-3d3a-4596-a3bc-dad00e67656f" void -init_cert_db(peverify_context *ctx, int use_system_dbs) +init_cert_db(pesigcheck_context *ctx, int use_system_dbs) { int rc = 0; @@ -139,36 +139,36 @@ init_cert_db(peverify_context *ctx, int use_system_dbs) rc = add_db_file(ctx, DB, DB_PATH, DB_EFIVAR); if (rc < 0 && errno != ENOENT) { - fprintf(stderr, "peverify: Could not add key database " + fprintf(stderr, "pesigcheck: Could not add key database " "\"%s\": %m\n", DB_PATH); exit(1); } rc = add_db_file(ctx, DB, MOK_PATH, DB_EFIVAR); if (rc < 0 && errno != ENOENT) { - fprintf(stderr, "peverify: Could not add key database " + fprintf(stderr, "pesigcheck: Could not add key database " "\"%s\": %m\n", MOK_PATH); exit(1); } if (ctx->db == NULL) { - fprintf(stderr, "peverify: warning: " + fprintf(stderr, "pesigcheck: warning: " "No key database available\n"); } rc = add_db_file(ctx, DBX, DBX_PATH, DB_EFIVAR); if (rc < 0 && errno != ENOENT) { - fprintf(stderr, "peverify: Could not add revocation " + fprintf(stderr, "pesigcheck: Could not add revocation " "database \"%s\": %m\n", DBX_PATH); exit(1); } } -typedef db_status (*checkfn)(peverify_context *ctx, SECItem *sig, +typedef db_status (*checkfn)(pesigcheck_context *ctx, SECItem *sig, efi_guid_t *sigtype, SECItem *pkcs7sig); static db_status -check_db(db_specifier which, peverify_context *ctx, checkfn check, +check_db(db_specifier which, pesigcheck_context *ctx, checkfn check, void *data, ssize_t datalen) { SECItem pkcs7sig, sig; @@ -217,7 +217,7 @@ check_db(db_specifier which, peverify_context *ctx, checkfn check, } static db_status -check_hash(peverify_context *ctx, SECItem *sig, efi_guid_t *sigtype, +check_hash(pesigcheck_context *ctx, SECItem *sig, efi_guid_t *sigtype, SECItem *pkcs7sig) { efi_guid_t efi_sha256 = EFI_CERT_SHA256_GUID; @@ -238,13 +238,13 @@ check_hash(peverify_context *ctx, SECItem *sig, efi_guid_t *sigtype, } db_status -check_db_hash(db_specifier which, peverify_context *ctx) +check_db_hash(db_specifier which, pesigcheck_context *ctx) { return check_db(which, ctx, check_hash, NULL, 0); } static db_status -check_cert(peverify_context *ctx, SECItem *sig, efi_guid_t *sigtype, +check_cert(pesigcheck_context *ctx, SECItem *sig, efi_guid_t *sigtype, SECItem *pkcs7sig) { SEC_PKCS7ContentInfo *cinfo = NULL; @@ -335,7 +335,7 @@ out: } db_status -check_db_cert(db_specifier which, peverify_context *ctx, void *data, ssize_t datalen) +check_db_cert(db_specifier which, pesigcheck_context *ctx, void *data, ssize_t datalen) { return check_db(which, ctx, check_cert, data, datalen); } diff --git a/src/certdb.h b/src/certdb.h index d64494d..ccf3c87 100644 --- a/src/certdb.h +++ b/src/certdb.h @@ -41,13 +41,13 @@ typedef struct { uint32_t SignatureSize; } EFI_SIGNATURE_LIST; -extern db_status check_db_hash(db_specifier which, peverify_context *ctx); -extern db_status check_db_cert(db_specifier which, peverify_context *ctx, +extern db_status check_db_hash(db_specifier which, pesigcheck_context *ctx); +extern db_status check_db_cert(db_specifier which, pesigcheck_context *ctx, void *data, ssize_t datalen); -extern void init_cert_db(peverify_context *ctx, int use_system_dbs); -extern int add_cert_db(peverify_context *ctx, const char *filename); -extern int add_cert_dbx(peverify_context *ctx, const char *filename); -extern int add_cert_file(peverify_context *ctx, const char *filename); +extern void init_cert_db(pesigcheck_context *ctx, int use_system_dbs); +extern int add_cert_db(pesigcheck_context *ctx, const char *filename); +extern int add_cert_dbx(pesigcheck_context *ctx, const char *filename); +extern int add_cert_file(pesigcheck_context *ctx, const char *filename); #endif /* CERTDB_H */ diff --git a/src/pesigcheck.1 b/src/pesigcheck.1 new file mode 100644 index 0000000..55101ab --- /dev/null +++ b/src/pesigcheck.1 @@ -0,0 +1,25 @@ +.TH pesigcheck 1 "Mon Sep 10 2012" +.SH NAME +pesign \- command line tool for verifying UEFI applications + +.SH SYNOPSIS +\fBpesign\fR [--in=\fIinfile\fR | -i \fIinfile\fR] [--quiet | -q ] + [--db=\fIdbfile\fR | -D \fIdbfile\fR ] + [--dbx=\fIdbxfile\fR | -X \fIdbxfile\fR ] + +.SH DESCRIPTION +\fBpesigcheck\fR is a command line tool for verifying the signature of UEFI +applications. + +.SH OPTIONS +.TP +\fB-\-in\fR=\fIinfile\fR +Specify input binary. + +.SH "SEE ALSO" +.BR pesigcheck (1) + +.SH AUTHORS +.nf +Peter Jones +.fi diff --git a/src/pesigcheck.c b/src/pesigcheck.c new file mode 100644 index 0000000..7cd98c9 --- /dev/null +++ b/src/pesigcheck.c @@ -0,0 +1,306 @@ +/* + * Copyright 2011-2012 Red Hat, Inc. + * All rights reserved. + * + * 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, see <http://www.gnu.org/licenses/>. + * + * Author(s): Peter Jones <pjones@redhat.com> + */ + +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <ftw.h> +#include <nss.h> + +#include <popt.h> + +#include <prerror.h> +#include <cert.h> +#include <pkcs7t.h> +#include <pk11pub.h> + +#include "pesigcheck.h" + +static void +open_input(pesigcheck_context *ctx) +{ + if (!ctx->infile) { + fprintf(stderr, "pesigcheck: No input file specified.\n"); + exit(1); + } + + ctx->infd = open(ctx->infile, O_RDONLY|O_CLOEXEC); + + if (ctx->infd < 0) { + fprintf(stderr, "pesigcheck: Error opening input: %m\n"); + exit(1); + } + + Pe_Cmd cmd = ctx->infd == STDIN_FILENO ? PE_C_READ : PE_C_READ_MMAP; + ctx->inpe = pe_begin(ctx->infd, cmd, NULL); + if (!ctx->inpe) { + fprintf(stderr, "pesigcheck: could not load input file: %s\n", + pe_errmsg(pe_errno())); + exit(1); + } + + int rc = parse_signatures(&ctx->cms_ctx->signatures, + &ctx->cms_ctx->num_signatures, + ctx->inpe); + if (rc < 0) { + fprintf(stderr, "pesigcheck: could not parse signature list in " + "EFI binary\n"); + exit(1); + } +} + +static void +close_input(pesigcheck_context *ctx) +{ + pe_end(ctx->inpe); + ctx->inpe = NULL; + + close(ctx->infd); + ctx->infd = -1; +} + +static void +check_inputs(pesigcheck_context *ctx) +{ + if (!ctx->infile) { + fprintf(stderr, "pesign: No input file specified.\n"); + exit(1); + } +} + +static int +cert_matches_digest(pesigcheck_context *ctx, void *data, ssize_t datalen) +{ + SECItem sig, *pe_digest, *content; + uint8_t *digest; + SEC_PKCS7ContentInfo *cinfo = NULL; + int ret = -1; + + sig.data = data; + sig.len = datalen; + sig.type = siBuffer; + + cinfo = SEC_PKCS7DecodeItem(&sig, NULL, NULL, NULL, NULL, NULL, + NULL, NULL); + + if (!SEC_PKCS7ContentIsSigned(cinfo)) + goto out; + + /* TODO Find out the digest type in spc_content */ + pe_digest = ctx->cms_ctx->digests[0].pe_digest; + content = cinfo->content.signedData->contentInfo.content.data; + digest = content->data + content->len - pe_digest->len; + if (memcmp(pe_digest->data, digest, pe_digest->len) != 0) + goto out; + + ret = 0; +out: + if (cinfo) + SEC_PKCS7DestroyContentInfo(cinfo); + + return ret; +} + +static int +check_signature(pesigcheck_context *ctx) +{ + int has_valid_cert = 0; + int has_invalid_cert = 0; + int rc = 0; + + cert_iter iter; + + generate_digest(ctx->cms_ctx, ctx->inpe, 1); + + if (check_db_hash(DBX, ctx) == FOUND) + return -1; + + if (check_db_hash(DB, ctx) == FOUND) + has_valid_cert = 1; + + rc = cert_iter_init(&iter, ctx->inpe); + if (rc < 0) + goto err; + + void *data; + ssize_t datalen; + + while (1) { + rc = next_cert(&iter, &data, &datalen); + if (rc <= 0) + break; + + if (cert_matches_digest(ctx, data, datalen) < 0) { + has_invalid_cert = 1; + break; + } + + if (check_db_cert(DBX, ctx, data, datalen) == FOUND) { + has_invalid_cert = 1; + break; + } + + if (check_db_cert(DB, ctx, data, datalen) == FOUND) + has_valid_cert = 1; + } + +err: + if (has_invalid_cert) + return -1; + + if (has_valid_cert) + return 0; + + return -1; +} + +void +callback(poptContext con, enum poptCallbackReason reason, + const struct poptOption *opt, + const char *arg, const void *data) +{ + pesigcheck_context *ctx = (pesigcheck_context *)data; + int rc = 0; + if (!opt) + return; + if (opt->shortName == 'D') { + rc = add_cert_db(ctx, arg); + } else if (opt->shortName == 'X') { + rc = add_cert_dbx(ctx, arg); + } else if (opt->shortName == 'c') { + rc = add_cert_file(ctx, arg); + } + if (rc != 0) { + fprintf(stderr, "Could not add %s from file \"%s\": %m\n", + opt->shortName == 'D' ? "DB" : "DBX", arg); + exit(1); + } +} + +static int +delete_files(const char *fpath, const struct stat *sb, int typeflag) +{ + if (typeflag == FTW_F) + remove(fpath); + + return 0; +} + +static void +remove_certdir(const char *certdir) +{ + ftw(certdir, delete_files, 3); + + remove(certdir); +} + +int +main(int argc, char *argv[]) +{ + int rc; + + pesigcheck_context ctx, *ctxp = &ctx; + + char *dbfile = NULL; + char *dbxfile = NULL; + char *certfile = NULL; + int use_system_dbs = 1; + + char template[] = "/tmp/pesigcheck-XXXXXX"; + char *certdir = NULL; + SECStatus status; + + poptContext optCon; + struct poptOption options[] = { + {"dbfile", 'D', POPT_ARG_CALLBACK|POPT_CBFLAG_POST, (void *)callback, 0, (void *)ctxp, NULL }, + {NULL, '\0', POPT_ARG_INTL_DOMAIN, "pesign" }, + {"in", 'i', POPT_ARG_STRING, &ctx.infile, 0, + "specify input file", "<infile>"}, + {"quiet", 'q', POPT_BIT_SET, &ctx.quiet, 1, + "return only; no text output.", NULL }, + {"no-system-db", 'n', POPT_ARG_INT, &use_system_dbs, 0, + "inhibit the use of DB and DBX from the running system", + NULL }, + {"dbfile", 'D', POPT_ARG_STRING, &dbfile, 0, + "use file for allowed certificate list", "<dbfile>" }, + {"dbxfile", 'X', POPT_ARG_STRING, &dbxfile, 0, + "use file for disallowed certificate list","<dbxfile>"}, + {"certfile", 'c', POPT_ARG_STRING, &certfile, 0, + "the certificate (in DER form) for verification ","<certfile>"}, + POPT_AUTOALIAS + POPT_AUTOHELP + POPT_TABLEEND + }; + + rc = pesigcheck_context_init(ctxp); + if (rc < 0) { + fprintf(stderr, "pesigcheck: Could not initialize context: %m\n"); + exit(1); + } + + optCon = poptGetContext("pesigcheck", argc, (const char **)argv, + options,0); + + while ((rc = poptGetNextOpt(optCon)) > 0) + ; + + if (rc < -1) { + fprintf(stderr, "pesigcheck: Invalid argument: %s: %s\n", + poptBadOption(optCon, 0), poptStrerror(rc)); + exit(1); + } + + if (poptPeekArg(optCon)) { + fprintf(stderr, "pesigcheck: Invalid Argument: \"%s\"\n", + poptPeekArg(optCon)); + exit(1); + } + + poptFreeContext(optCon); + + check_inputs(ctxp); + open_input(ctxp); + + init_cert_db(ctxp, use_system_dbs); + + certdir = mkdtemp(template); + status = NSS_InitReadWrite(certdir); + if (status != SECSuccess) { + fprintf(stderr, "Could not initialize nss: %s\n", + PORT_ErrorToString(PORT_GetError())); + exit(1); + } + + rc = check_signature(ctxp); + + close_input(ctxp); + if (!ctx.quiet) + printf("pesigcheck: \"%s\" is %s.\n", ctx.infile, + rc >= 0 ? "valid" : "invalid"); + pesigcheck_context_fini(&ctx); + + NSS_Shutdown(); + remove_certdir(certdir); + + return (rc < 0); +} diff --git a/src/pesigcheck.h b/src/pesigcheck.h new file mode 100644 index 0000000..ee0b63c --- /dev/null +++ b/src/pesigcheck.h @@ -0,0 +1,39 @@ +/* + * Copyright 2011 Red Hat, Inc. + * All rights reserved. + * + * 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, see <http://www.gnu.org/licenses/>. + * + * Author(s): Peter Jones <pjones@redhat.com> + */ +#ifndef PESIGN_H +#define PESIGN_H 1 + +#include <libdpe/libdpe.h> +#include <libdpe/pe.h> + +#include "efitypes.h" +#include "cms_common.h" +#include "pesigcheck_context.h" +#include "certdb.h" + +#include "util.h" +#include "endian.h" +#include "oid.h" +#include "wincert.h" +#include "content_info.h" +#include "signer_info.h" +#include "signed_data.h" +#include "password.h" + +#endif /* PESIGN_H */ diff --git a/src/pesigcheck_context.c b/src/pesigcheck_context.c new file mode 100644 index 0000000..b934cbe --- /dev/null +++ b/src/pesigcheck_context.c @@ -0,0 +1,122 @@ +/* + * Copyright 2012 Red Hat, Inc. + * All rights reserved. + * + * 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, see <http://www.gnu.org/licenses/>. + * + * Author(s): Peter Jones <pjones@redhat.com> + */ + +#include <sys/mman.h> +#include <unistd.h> + +#include "pesigcheck.h" + +#include <nss.h> +#include <secitem.h> + +int +pesigcheck_context_new(pesigcheck_context **ctx) +{ + pesigcheck_context *context = NULL; + int rc = 0; + + if (ctx == NULL) + return -1; + + context = malloc(sizeof (*context)); + if (!context) + return -1; + + pesigcheck_context_init(context); + context->flags |= pesigcheck_C_ALLOCATED; + + *ctx = context; + return rc; +} + +int +pesigcheck_context_init(pesigcheck_context *ctx) +{ + if (!ctx) + return -1; + memset(ctx, '\0', sizeof (*ctx)); + + ctx->infd = -1; + + int rc = cms_context_alloc(&ctx->cms_ctx); + if (rc < 0) + return rc; + + return 0; +} + +void +pesigcheck_context_fini(pesigcheck_context *ctx) +{ + if (!ctx) + return; + + cms_context_fini(ctx->cms_ctx); + + xfree(ctx->infile); + + if (ctx->inpe) { + pe_end(ctx->inpe); + ctx->inpe = NULL; + } + + if (!(ctx->flags & pesigcheck_C_ALLOCATED)) + pesigcheck_context_init(ctx); + + while (ctx->db) { + dblist *db = ctx->db; + + if (db->type == DB_CERT) + free(db->data); + munmap(db->map, db->size); + close(db->fd); + ctx->db = db->next; + free(db); + } + while (ctx->dbx) { + dblist *db = ctx->dbx; + + if (db->type == DB_CERT) + free(db->data); + munmap(db->map, db->size); + close(db->fd); + ctx->dbx = db->next; + free(db); + } + while (ctx->hashes) { + hashlist *hashes = ctx->hashes; + free(hashes->data); + ctx->hashes = hashes->next; + free(hashes); + } +} + +void +pesigcheck_context_free_private(pesigcheck_context **ctx_ptr) +{ + pesigcheck_context *ctx; + if (!ctx_ptr || !*ctx_ptr) + return; + + ctx = *ctx_ptr; + pesigcheck_context_fini(ctx); + + if (ctx->flags & pesigcheck_C_ALLOCATED) + xfree(*ctx_ptr); +} diff --git a/src/pesigcheck_context.h b/src/pesigcheck_context.h new file mode 100644 index 0000000..1b916e3 --- /dev/null +++ b/src/pesigcheck_context.h @@ -0,0 +1,78 @@ +/* + * Copyright 2012 Red Hat, Inc. + * All rights reserved. + * + * 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, see <http://www.gnu.org/licenses/>. + * + * Author(s): Peter Jones <pjones@redhat.com> + */ +#ifndef pesigcheck_CONTEXT_H +#define pesigcheck_CONTEXT_H 1 + +#include <cert.h> +#include <secpkcs7.h> + +enum { + pesigcheck_C_ALLOCATED = 1, +}; + +typedef enum { + DB_FILE, + DB_EFIVAR, + DB_CERT, +} db_f_type; + +struct dblist { + db_f_type type; + int fd; + struct dblist *next; + size_t size; + void *map; + size_t datalen; + void *data; +}; + +typedef struct dblist dblist; + +struct hashlist { + efi_guid_t *hash_type; + void *data; + size_t datalen; + struct hashlist *next; +}; +typedef struct hashlist hashlist; + +typedef struct pesigcheck_context { + int flags; + + char *infile; + int infd; + Pe *inpe; + + int quiet; + + hashlist *hashes; + + dblist *db; + dblist *dbx; + + cms_context *cms_ctx; +} pesigcheck_context; + +extern int pesigcheck_context_new(pesigcheck_context **ctx); +extern void pesigcheck_context_free_private(pesigcheck_context **ctx_ptr); +extern int pesigcheck_context_init(pesigcheck_context *ctx); +extern void pesigcheck_context_fini(pesigcheck_context *ctx); +#define pesigcheck_context_free(ctx) pesigcheck_context_free_private(&(ctx)) + +#endif /* pesigcheck_CONTEXT_H */ diff --git a/src/peverify.1 b/src/peverify.1 deleted file mode 100644 index ce8da2a..0000000 --- a/src/peverify.1 +++ /dev/null @@ -1,25 +0,0 @@ -.TH PEVERIFY 1 "Mon Sep 10 2012" -.SH NAME -pesign \- command line tool for verifying UEFI applications - -.SH SYNOPSIS -\fBpesign\fR [--in=\fIinfile\fR | -i \fIinfile\fR] [--quiet | -q ] - [--db=\fIdbfile\fR | -D \fIdbfile\fR ] - [--dbx=\fIdbxfile\fR | -X \fIdbxfile\fR ] - -.SH DESCRIPTION -\fBpeverify\fR is a command line tool for verifying the signature of UEFI -applications. - -.SH OPTIONS -.TP -\fB-\-in\fR=\fIinfile\fR -Specify input binary. - -.SH "SEE ALSO" -.BR peverify (1) - -.SH AUTHORS -.nf -Peter Jones -.fi diff --git a/src/peverify.c b/src/peverify.c deleted file mode 100644 index ebd7ee7..0000000 --- a/src/peverify.c +++ /dev/null @@ -1,306 +0,0 @@ -/* - * Copyright 2011-2012 Red Hat, Inc. - * All rights reserved. - * - * 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, see <http://www.gnu.org/licenses/>. - * - * Author(s): Peter Jones <pjones@redhat.com> - */ - -#include <fcntl.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <ftw.h> -#include <nss.h> - -#include <popt.h> - -#include <prerror.h> -#include <cert.h> -#include <pkcs7t.h> -#include <pk11pub.h> - -#include "peverify.h" - -static void -open_input(peverify_context *ctx) -{ - if (!ctx->infile) { - fprintf(stderr, "peverify: No input file specified.\n"); - exit(1); - } - - ctx->infd = open(ctx->infile, O_RDONLY|O_CLOEXEC); - - if (ctx->infd < 0) { - fprintf(stderr, "peverify: Error opening input: %m\n"); - exit(1); - } - - Pe_Cmd cmd = ctx->infd == STDIN_FILENO ? PE_C_READ : PE_C_READ_MMAP; - ctx->inpe = pe_begin(ctx->infd, cmd, NULL); - if (!ctx->inpe) { - fprintf(stderr, "peverify: could not load input file: %s\n", - pe_errmsg(pe_errno())); - exit(1); - } - - int rc = parse_signatures(&ctx->cms_ctx->signatures, - &ctx->cms_ctx->num_signatures, - ctx->inpe); - if (rc < 0) { - fprintf(stderr, "peverify: could not parse signature list in " - "EFI binary\n"); - exit(1); - } -} - -static void -close_input(peverify_context *ctx) -{ - pe_end(ctx->inpe); - ctx->inpe = NULL; - - close(ctx->infd); - ctx->infd = -1; -} - -static void -check_inputs(peverify_context *ctx) -{ - if (!ctx->infile) { - fprintf(stderr, "pesign: No input file specified.\n"); - exit(1); - } -} - -static int -cert_matches_digest(peverify_context *ctx, void *data, ssize_t datalen) -{ - SECItem sig, *pe_digest, *content; - uint8_t *digest; - SEC_PKCS7ContentInfo *cinfo = NULL; - int ret = -1; - - sig.data = data; - sig.len = datalen; - sig.type = siBuffer; - - cinfo = SEC_PKCS7DecodeItem(&sig, NULL, NULL, NULL, NULL, NULL, - NULL, NULL); - - if (!SEC_PKCS7ContentIsSigned(cinfo)) - goto out; - - /* TODO Find out the digest type in spc_content */ - pe_digest = ctx->cms_ctx->digests[0].pe_digest; - content = cinfo->content.signedData->contentInfo.content.data; - digest = content->data + content->len - pe_digest->len; - if (memcmp(pe_digest->data, digest, pe_digest->len) != 0) - goto out; - - ret = 0; -out: - if (cinfo) - SEC_PKCS7DestroyContentInfo(cinfo); - - return ret; -} - -static int -check_signature(peverify_context *ctx) -{ - int has_valid_cert = 0; - int has_invalid_cert = 0; - int rc = 0; - - cert_iter iter; - - generate_digest(ctx->cms_ctx, ctx->inpe, 1); - - if (check_db_hash(DBX, ctx) == FOUND) - return -1; - - if (check_db_hash(DB, ctx) == FOUND) - has_valid_cert = 1; - - rc = cert_iter_init(&iter, ctx->inpe); - if (rc < 0) - goto err; - - void *data; - ssize_t datalen; - - while (1) { - rc = next_cert(&iter, &data, &datalen); - if (rc <= 0) - break; - - if (cert_matches_digest(ctx, data, datalen) < 0) { - has_invalid_cert = 1; - break; - } - - if (check_db_cert(DBX, ctx, data, datalen) == FOUND) { - has_invalid_cert = 1; - break; - } - - if (check_db_cert(DB, ctx, data, datalen) == FOUND) - has_valid_cert = 1; - } - -err: - if (has_invalid_cert) - return -1; - - if (has_valid_cert) - return 0; - - return -1; -} - -void -callback(poptContext con, enum poptCallbackReason reason, - const struct poptOption *opt, - const char *arg, const void *data) -{ - peverify_context *ctx = (peverify_context *)data; - int rc = 0; - if (!opt) - return; - if (opt->shortName == 'D') { - rc = add_cert_db(ctx, arg); - } else if (opt->shortName == 'X') { - rc = add_cert_dbx(ctx, arg); - } else if (opt->shortName == 'c') { - rc = add_cert_file(ctx, arg); - } - if (rc != 0) { - fprintf(stderr, "Could not add %s from file \"%s\": %m\n", - opt->shortName == 'D' ? "DB" : "DBX", arg); - exit(1); - } -} - -static int -delete_files(const char *fpath, const struct stat *sb, int typeflag) -{ - if (typeflag == FTW_F) - remove(fpath); - - return 0; -} - -static void -remove_certdir(const char *certdir) -{ - ftw(certdir, delete_files, 3); - - remove(certdir); -} - -int -main(int argc, char *argv[]) -{ - int rc; - - peverify_context ctx, *ctxp = &ctx; - - char *dbfile = NULL; - char *dbxfile = NULL; - char *certfile = NULL; - int use_system_dbs = 1; - - char template[] = "/tmp/peverify-XXXXXX"; - char *certdir = NULL; - SECStatus status; - - poptContext optCon; - struct poptOption options[] = { - {"dbfile", 'D', POPT_ARG_CALLBACK|POPT_CBFLAG_POST, (void *)callback, 0, (void *)ctxp, NULL }, - {NULL, '\0', POPT_ARG_INTL_DOMAIN, "pesign" }, - {"in", 'i', POPT_ARG_STRING, &ctx.infile, 0, - "specify input file", "<infile>"}, - {"quiet", 'q', POPT_BIT_SET, &ctx.quiet, 1, - "return only; no text output.", NULL }, - {"no-system-db", 'n', POPT_ARG_INT, &use_system_dbs, 0, - "inhibit the use of DB and DBX from the running system", - NULL }, - {"dbfile", 'D', POPT_ARG_STRING, &dbfile, 0, - "use file for allowed certificate list", "<dbfile>" }, - {"dbxfile", 'X', POPT_ARG_STRING, &dbxfile, 0, - "use file for disallowed certificate list","<dbxfile>"}, - {"certfile", 'c', POPT_ARG_STRING, &certfile, 0, - "the certificate (in DER form) for verification ","<certfile>"}, - POPT_AUTOALIAS - POPT_AUTOHELP - POPT_TABLEEND - }; - - rc = peverify_context_init(ctxp); - if (rc < 0) { - fprintf(stderr, "peverify: Could not initialize context: %m\n"); - exit(1); - } - - optCon = poptGetContext("peverify", argc, (const char **)argv, - options,0); - - while ((rc = poptGetNextOpt(optCon)) > 0) - ; - - if (rc < -1) { - fprintf(stderr, "peverify: Invalid argument: %s: %s\n", - poptBadOption(optCon, 0), poptStrerror(rc)); - exit(1); - } - - if (poptPeekArg(optCon)) { - fprintf(stderr, "peverify: Invalid Argument: \"%s\"\n", - poptPeekArg(optCon)); - exit(1); - } - - poptFreeContext(optCon); - - check_inputs(ctxp); - open_input(ctxp); - - init_cert_db(ctxp, use_system_dbs); - - certdir = mkdtemp(template); - status = NSS_InitReadWrite(certdir); - if (status != SECSuccess) { - fprintf(stderr, "Could not initialize nss: %s\n", - PORT_ErrorToString(PORT_GetError())); - exit(1); - } - - rc = check_signature(ctxp); - - close_input(ctxp); - if (!ctx.quiet) - printf("peverify: \"%s\" is %s.\n", ctx.infile, - rc >= 0 ? "valid" : "invalid"); - peverify_context_fini(&ctx); - - NSS_Shutdown(); - remove_certdir(certdir); - - return (rc < 0); -} diff --git a/src/peverify.h b/src/peverify.h deleted file mode 100644 index 572c3ef..0000000 --- a/src/peverify.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2011 Red Hat, Inc. - * All rights reserved. - * - * 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, see <http://www.gnu.org/licenses/>. - * - * Author(s): Peter Jones <pjones@redhat.com> - */ -#ifndef PESIGN_H -#define PESIGN_H 1 - -#include <libdpe/libdpe.h> -#include <libdpe/pe.h> - -#include "efitypes.h" -#include "cms_common.h" -#include "peverify_context.h" -#include "certdb.h" - -#include "util.h" -#include "endian.h" -#include "oid.h" -#include "wincert.h" -#include "content_info.h" -#include "signer_info.h" -#include "signed_data.h" -#include "password.h" - -#endif /* PESIGN_H */ diff --git a/src/peverify_context.c b/src/peverify_context.c deleted file mode 100644 index d3aa53e..0000000 --- a/src/peverify_context.c +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright 2012 Red Hat, Inc. - * All rights reserved. - * - * 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, see <http://www.gnu.org/licenses/>. - * - * Author(s): Peter Jones <pjones@redhat.com> - */ - -#include <sys/mman.h> -#include <unistd.h> - -#include "peverify.h" - -#include <nss.h> -#include <secitem.h> - -int -peverify_context_new(peverify_context **ctx) -{ - peverify_context *context = NULL; - int rc = 0; - - if (ctx == NULL) - return -1; - - context = malloc(sizeof (*context)); - if (!context) - return -1; - - peverify_context_init(context); - context->flags |= PEVERIFY_C_ALLOCATED; - - *ctx = context; - return rc; -} - -int -peverify_context_init(peverify_context *ctx) -{ - if (!ctx) - return -1; - memset(ctx, '\0', sizeof (*ctx)); - - ctx->infd = -1; - - int rc = cms_context_alloc(&ctx->cms_ctx); - if (rc < 0) - return rc; - - return 0; -} - -void -peverify_context_fini(peverify_context *ctx) -{ - if (!ctx) - return; - - cms_context_fini(ctx->cms_ctx); - - xfree(ctx->infile); - - if (ctx->inpe) { - pe_end(ctx->inpe); - ctx->inpe = NULL; - } - - if (!(ctx->flags & PEVERIFY_C_ALLOCATED)) - peverify_context_init(ctx); - - while (ctx->db) { - dblist *db = ctx->db; - - if (db->type == DB_CERT) - free(db->data); - munmap(db->map, db->size); - close(db->fd); - ctx->db = db->next; - free(db); - } - while (ctx->dbx) { - dblist *db = ctx->dbx; - - if (db->type == DB_CERT) - free(db->data); - munmap(db->map, db->size); - close(db->fd); - ctx->dbx = db->next; - free(db); - } - while (ctx->hashes) { - hashlist *hashes = ctx->hashes; - free(hashes->data); - ctx->hashes = hashes->next; - free(hashes); - } -} - -void -peverify_context_free_private(peverify_context **ctx_ptr) -{ - peverify_context *ctx; - if (!ctx_ptr || !*ctx_ptr) - return; - - ctx = *ctx_ptr; - peverify_context_fini(ctx); - - if (ctx->flags & PEVERIFY_C_ALLOCATED) - xfree(*ctx_ptr); -} diff --git a/src/peverify_context.h b/src/peverify_context.h deleted file mode 100644 index 7e26d06..0000000 --- a/src/peverify_context.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2012 Red Hat, Inc. - * All rights reserved. - * - * 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, see <http://www.gnu.org/licenses/>. - * - * Author(s): Peter Jones <pjones@redhat.com> - */ -#ifndef PEVERIFY_CONTEXT_H -#define PEVERIFY_CONTEXT_H 1 - -#include <cert.h> -#include <secpkcs7.h> - -enum { - PEVERIFY_C_ALLOCATED = 1, -}; - -typedef enum { - DB_FILE, - DB_EFIVAR, - DB_CERT, -} db_f_type; - -struct dblist { - db_f_type type; - int fd; - struct dblist *next; - size_t size; - void *map; - size_t datalen; - void *data; -}; - -typedef struct dblist dblist; - -struct hashlist { - efi_guid_t *hash_type; - void *data; - size_t datalen; - struct hashlist *next; -}; -typedef struct hashlist hashlist; - -typedef struct peverify_context { - int flags; - - char *infile; - int infd; - Pe *inpe; - - int quiet; - - hashlist *hashes; - - dblist *db; - dblist *dbx; - - cms_context *cms_ctx; -} peverify_context; - -extern int peverify_context_new(peverify_context **ctx); -extern void peverify_context_free_private(peverify_context **ctx_ptr); -extern int peverify_context_init(peverify_context *ctx); -extern void peverify_context_fini(peverify_context *ctx); -#define peverify_context_free(ctx) peverify_context_free_private(&(ctx)) - -#endif /* PEVERIFY_CONTEXT_H */ -- 1.8.4.5 From 4191f24b18f1bf2a7be5da498b36f016bf115919 Mon Sep 17 00:00:00 2001 From: Gary Ching-Pang Lin <glin@suse.com> Date: Tue, 7 Jan 2014 12:02:47 +0800 Subject: [PATCH 10/31] Drop the temporary nss dir in pesigcheck I thought we need a "physical" database for the certificates but it's actually not necessary. Drop the nss dir creation/deletion code to avoid the extra file system access. Signed-off-by: Gary Ching-Pang Lin <glin@suse.com> --- src/pesigcheck.c | 24 +----------------------- 1 file changed, 1 insertion(+), 23 deletions(-) diff --git a/src/pesigcheck.c b/src/pesigcheck.c index 7cd98c9..9cf33be 100644 --- a/src/pesigcheck.c +++ b/src/pesigcheck.c @@ -24,7 +24,6 @@ #include <unistd.h> #include <sys/stat.h> #include <sys/types.h> -#include <ftw.h> #include <nss.h> #include <popt.h> @@ -197,23 +196,6 @@ callback(poptContext con, enum poptCallbackReason reason, } } -static int -delete_files(const char *fpath, const struct stat *sb, int typeflag) -{ - if (typeflag == FTW_F) - remove(fpath); - - return 0; -} - -static void -remove_certdir(const char *certdir) -{ - ftw(certdir, delete_files, 3); - - remove(certdir); -} - int main(int argc, char *argv[]) { @@ -226,8 +208,6 @@ main(int argc, char *argv[]) char *certfile = NULL; int use_system_dbs = 1; - char template[] = "/tmp/pesigcheck-XXXXXX"; - char *certdir = NULL; SECStatus status; poptContext optCon; @@ -283,8 +263,7 @@ main(int argc, char *argv[]) init_cert_db(ctxp, use_system_dbs); - certdir = mkdtemp(template); - status = NSS_InitReadWrite(certdir); + status = NSS_NoDB_Init(NULL); if (status != SECSuccess) { fprintf(stderr, "Could not initialize nss: %s\n", PORT_ErrorToString(PORT_GetError())); @@ -300,7 +279,6 @@ main(int argc, char *argv[]) pesigcheck_context_fini(&ctx); NSS_Shutdown(); - remove_certdir(certdir); return (rc < 0); } -- 1.8.4.5 From c61386706b169ec02f55880a11dd8097b68d6180 Mon Sep 17 00:00:00 2001 From: Gary Ching-Pang Lin <glin@suse.com> Date: Wed, 8 Jan 2014 14:17:30 +0800 Subject: [PATCH 11/31] efisiglist: convert the hex array properly Signed-off-by: Gary Ching-Pang Lin <glin@suse.com> --- src/efisiglist.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/efisiglist.c b/src/efisiglist.c index b7190cb..e01ab73 100644 --- a/src/efisiglist.c +++ b/src/efisiglist.c @@ -70,9 +70,9 @@ static int8_t hexchar_to_bin(char hex) if (hex >= '0' && hex <= '9') return hex - '0'; if (hex >= 'A' && hex <= 'F') - return hex - 'A'; + return hex - 'A' + 10; if (hex >= 'a' && hex <= 'f') - return hex - 'a'; + return hex - 'a' + 10; return -1; } @@ -83,7 +83,7 @@ hex_to_bin(char *hex, size_t size) if (!ret) return NULL; - for (int i = 0, j = 0; i < size; i+= 2, j++) { + for (int i = 0, j = 0; i < size*2; i+= 2, j++) { uint8_t val; val = hexchar_to_bin(hex[i]); @@ -94,7 +94,7 @@ out_of_range: return NULL; } ret[j] = (val & 0xf) << 4; - val = hexchar_to_bin(hex[i]); + val = hexchar_to_bin(hex[i+1]); if (val < 0) goto out_of_range; ret[j] |= val & 0xf; -- 1.8.4.5 From 65b8b80de336920cb464d5b5881a66bbeebaa343 Mon Sep 17 00:00:00 2001 From: Gary Ching-Pang Lin <glin@suse.com> Date: Wed, 8 Jan 2014 14:20:38 +0800 Subject: [PATCH 12/31] efisiglist: Correct the calulation of SignatureListSize Signed-off-by: Gary Ching-Pang Lin <glin@suse.com> --- src/siglist.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/siglist.c b/src/siglist.c index ca097e6..0457208 100644 --- a/src/siglist.c +++ b/src/siglist.c @@ -100,6 +100,7 @@ signature_list_new(efi_guid_t SignatureType) sl->SignatureType = SignatureType; sl->SignatureSize = size + sizeof (efi_guid_t); + sl->SignatureListSize = sizeof (struct efi_signature_list); return sl; } @@ -107,7 +108,8 @@ signature_list_new(efi_guid_t SignatureType) static int resize_entries(signature_list *sl, uint32_t newsize) { - for (int i = 0; i < sl->SignatureListSize; i++) { + int count = (sl->SignatureListSize - sizeof (struct efi_signature_list)) / sl->SignatureSize; + for (int i = 0; i < count; i++) { struct efi_signature_data *sd = sl->Signatures[i]; struct efi_signature_data *new_sd = calloc(1, newsize); @@ -118,7 +120,8 @@ resize_entries(signature_list *sl, uint32_t newsize) free(sd); sl->Signatures[i] = sd; } - sl->SignatureListSize = newsize; + sl->SignatureSize = newsize; + sl->SignatureListSize = sizeof (struct efi_signature_list) + count * newsize; return 0; } @@ -151,17 +154,17 @@ signature_list_add_sig(signature_list *sl, efi_guid_t owner, memcpy(sd->SignatureData, sig, sl->SignatureSize - sizeof (efi_guid_t)); - struct efi_signature_data **sdl = calloc(sl->SignatureListSize+1, + int count = (sl->SignatureListSize - sizeof (struct efi_signature_list)) / sl->SignatureSize; + struct efi_signature_data **sdl = calloc(count+1, sizeof (struct efi_signature_data *)); if (!sdl) { free(sd); return -1; } - memcpy(sdl, sl->Signatures, sl->SignatureListSize * - sizeof (struct efi_signature_data *)); - sdl[sl->SignatureListSize] = sd; - sl->SignatureListSize++; + memcpy(sdl, sl->Signatures, count * sl->SignatureSize); + sdl[count] = sd; + sl->SignatureListSize += sl->SignatureSize; free(sl->Signatures); sl->Signatures = sdl; @@ -195,9 +198,10 @@ signature_list_realize(signature_list *sl, void **out, size_t *outsize) sl->realized = NULL; } + int count = (sl->SignatureListSize - sizeof (struct efi_signature_list)) / sl->SignatureSize; struct efi_signature_list *esl = NULL; uint32_t size = sizeof (*esl) + - + sl->SignatureListSize * sl->SignatureSize; + + count * sl->SignatureSize; void *ret = calloc(1, size); if (!ret) @@ -207,7 +211,7 @@ signature_list_realize(signature_list *sl, void **out, size_t *outsize) memcpy(esl, sl, sizeof (*esl)); uint8_t *pos = ret + sizeof (*esl); - for (int i = 0; i < sl->SignatureListSize; i++) { + for (int i = 0; i < count; i++) { memcpy(pos, sl->Signatures[i], sl->SignatureSize); pos += sl->SignatureSize; } @@ -225,7 +229,8 @@ signature_list_free(signature_list *sl) if (sl->realized) free(sl->realized); - for (int i = 0; i < sl->SignatureListSize; i++) + int count = (sl->SignatureListSize - sizeof (struct efi_signature_list)) / sl->SignatureSize; + for (int i = 0; i < count; i++) free(sl->Signatures[i]); free(sl); -- 1.8.4.5 From b51e250f52fe599cf1713c3c91a4b29f0b73fc4c Mon Sep 17 00:00:00 2001 From: Gary Ching-Pang Lin <glin@suse.com> Date: Wed, 8 Jan 2014 15:10:18 +0800 Subject: [PATCH 13/31] efisiglist: support adding a certificate in DER form Signed-off-by: Gary Ching-Pang Lin <glin@suse.com> --- src/efisiglist.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 70 insertions(+), 2 deletions(-) diff --git a/src/efisiglist.c b/src/efisiglist.c index e01ab73..b96553b 100644 --- a/src/efisiglist.c +++ b/src/efisiglist.c @@ -23,6 +23,9 @@ #include <stdlib.h> #include <string.h> #include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mman.h> #include "efitypes.h" #include "siglist.h" @@ -106,11 +109,15 @@ int main(int argc, char *argv[]) { poptContext optCon; + efi_guid_t owner = RH_GUID; int rc; char *outfile = NULL; char *hash = NULL; char *hash_type = "sha256"; char *certfile = NULL; + int certfd = -1; + void *cert_data = NULL; + size_t cert_size = 0; int add = 1; @@ -126,7 +133,7 @@ main(int argc, char *argv[]) "hash value to add", "<hash>" }, {"hash-type", 't', POPT_ARG_STRING|POPT_ARGFLAG_SHOW_DEFAULT, &hash_type, 0, "hash type to add", "<hash-type>" }, - {"certificate", 'c', POPT_ARG_STRING|POPT_ARGFLAG_DOC_HIDDEN, + {"certificate", 'c', POPT_ARG_STRING, &certfile, 0, "certificate to add", "<certfile>" }, POPT_AUTOALIAS POPT_AUTOHELP @@ -197,6 +204,29 @@ main(int argc, char *argv[]) hash_params[hash_index].size * 8, x * 4); exit(1); } + } else if (certfile) { + certfd = open(certfile, O_RDONLY, 0644); + if (certfd < 0) { + fprintf(stderr, "efisiglist: could not open \"%s\": " + "%m\n", certfile); + exit(1); + } + + struct stat sb; + if (fstat(certfd, &sb) < 0) { + fprintf(stderr, "efisiglist: could not get the size " + "of \"%s\": %m\n", certfile); + exit(1); + } + cert_size = sb.st_size; + + cert_data = mmap(NULL, cert_size, PROT_READ, MAP_PRIVATE, + certfd, 0); + if (cert_data == MAP_FAILED) { + fprintf(stderr, "efisiglist: could not map \"%s\": " + "%m\n", certfile); + exit(1); + } } if (add) { @@ -209,7 +239,6 @@ main(int argc, char *argv[]) unlink(outfile); exit(1); } - efi_guid_t owner = RH_GUID; uint8_t *binary_hash = hex_to_bin(hash, hash_params[hash_index].size); if (!binary_hash) { @@ -245,6 +274,45 @@ main(int argc, char *argv[]) } close(outfd); exit(0); + } else if (certfile) { + efi_guid_t sig_type = EFI_CERT_X509_GUID; + signature_list *sl = signature_list_new(sig_type); + if (!sl) { + fprintf(stderr, "efisiglist: could not " + "allocate signature list: %m\n"); + unlink(outfile); + exit(1); + } + rc = signature_list_add_sig(sl, owner, cert_data, + cert_size); + if (rc < 0) { + fprintf(stderr,"efisiglist: could not add " + "cert to list: %m\n"); + unlink(outfile); + exit(1); + } + + void *blah; + size_t size = 0; + rc = signature_list_realize(sl, &blah, &size); + if (rc < 0) { + fprintf(stderr, "efisiglist: Could not realize " + "signature list: %m\n"); + unlink(outfile); + exit(1); + } + rc = write(outfd, blah, size); + if (rc < 0) { + fprintf(stderr, "efisiglist: Could not write " + "signature list: %m\n"); + unlink(outfile); + exit(1); + } + + munmap(cert_data, cert_size); + close(certfd); + close(outfd); + exit(0); } } exit(1); -- 1.8.4.5 From a2a7e57e1786a65bac95d1ce03ceda0487c9c2bf Mon Sep 17 00:00:00 2001 From: Michael Scherer <misc@zarb.org> Date: Mon, 6 Jan 2014 00:48:54 +0100 Subject: [PATCH 14/31] Fix incorrect assignation, and fix memleak ( since new_sd is allocated and never used ) --- src/siglist.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/siglist.c b/src/siglist.c index 0457208..e001493 100644 --- a/src/siglist.c +++ b/src/siglist.c @@ -118,7 +118,7 @@ resize_entries(signature_list *sl, uint32_t newsize) memcpy(new_sd, sd, sl->SignatureSize); free(sd); - sl->Signatures[i] = sd; + sl->Signatures[i] = new_sd; } sl->SignatureSize = newsize; sl->SignatureListSize = sizeof (struct efi_signature_list) + count * newsize; -- 1.8.4.5 From 3e3f152387dfc54598c29b5db7540fad9a9043d8 Mon Sep 17 00:00:00 2001 From: Gary Ching-Pang Lin <glin@suse.com> Date: Fri, 30 May 2014 18:16:53 +0800 Subject: [PATCH 15/31] authvar: fill some baisc functions Signed-off-by: Gary Ching-Pang Lin <glin@suse.com> --- src/authvar.c | 223 ++++++++++++++++++++++++++++++++++++++++++++------ src/authvar.h | 1 + src/authvar_context.c | 148 ++++++++++++++++++++++++++++++++- src/authvar_context.h | 20 ++++- src/efitypes.h | 4 +- src/wincert.h | 13 +++ 6 files changed, 375 insertions(+), 34 deletions(-) diff --git a/src/authvar.c b/src/authvar.c index 1a05e6b..cfa9de3 100644 --- a/src/authvar.c +++ b/src/authvar.c @@ -19,6 +19,17 @@ #include <errno.h> #include <popt.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/mman.h> +#include <string.h> +#include <time.h> +#include <stdlib.h> + +#include <prerror.h> +#include <nss.h> #include "authvar.h" @@ -86,6 +97,75 @@ check_value(authvar_context *ctx, int needed) "authvar: command does not take a value.\n"); exit(1); } + + if (ctx->value) { + ctx->value_size = strlen(ctx->value); + } +} + +static void +open_input(authvar_context *ctx) +{ + struct stat sb; + + if (!ctx->valuefile) + return; + + ctx->valuefd = open(ctx->valuefile, O_RDONLY|O_CLOEXEC); + if (ctx->valuefd < 0) { + fprintf(stderr, "authvar: Error opening valuefile: %m\n"); + exit(1); + } + + if (fstat(ctx->valuefd, &sb) < 0) { + fprintf(stderr, "authvar: Error mapping valuefile: %m\n"); + exit(1); + } + ctx->value_size = sb.st_size; + + ctx->value = (char *)mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, + ctx->valuefd, 0); + if (ctx->value == MAP_FAILED) { + fprintf(stderr, "authvar: Error mapping valuefile: %m\n"); + exit(1); + } +} + +#define EFIVAR_DIR "/sys/firmware/efi/efivars/" + +static void +generate_efivars_filename(authvar_context *ctx) +{ + efi_guid_t guid = ctx->guid; + size_t length; + + length = strlen(EFIVAR_DIR) + strlen(ctx->name) + 38; + ctx->exportfile = (char *)malloc(length); + + sprintf(ctx->exportfile, "%s%s-%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", + EFIVAR_DIR, ctx->name, guid.data1, guid.data2, guid.data3, + guid.data4[0], guid.data4[1], guid.data4[2], + guid.data4[3], guid.data4[4], guid.data4[5], + guid.data4[6], guid.data4[7]); +} + +static void +open_output(authvar_context *ctx) +{ + int flags; + mode_t mode; + + if (!ctx->exportfile) { + generate_efivars_filename(ctx); + } + + flags = O_CREAT|O_RDWR|O_CLOEXEC; + mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; + ctx->exportfd = open(ctx->exportfile, flags, mode); + if (ctx->exportfd < 0) { + fprintf(stderr, "authvar: Error opening exportfile: %m\n"); + exit(1); + } } static int @@ -163,11 +243,41 @@ find_namespace_guid(authvar_context *ctx) return parse_guid(ctx->namespace, &ctx->guid); } +static void +set_timestamp(authvar_context *ctx, const char *time_str) +{ + time_t t; + struct tm *tm; + + if (time_str) { + /* TODO parse the string */ + } else { + time(&t); + tm = gmtime(&t); + } + + ctx->timestamp.year = tm->tm_year + 1900; + ctx->timestamp.month = tm->tm_mon + 1; + ctx->timestamp.day = tm->tm_mday; + ctx->timestamp.hour = tm->tm_hour; + ctx->timestamp.minute = tm->tm_min; + ctx->timestamp.second = tm->tm_sec; + + ctx->timestamp.pad1 = 0; + ctx->timestamp.nanosecond = 0; + ctx->timestamp.timezone = 0; + ctx->timestamp.daylight = 0; + ctx->timestamp.pad2 = 0; +} + int main(int argc, char *argv[]) { int rc; authvar_context ctx = { 0, }; authvar_context *ctxp = &ctx; + char *time_str = NULL; + char *certdir = "/etc/pki/pesign"; + SECStatus status; int action = 0; @@ -182,6 +292,10 @@ int main(int argc, char *argv[]) { NULL, '\0', POPT_ARG_INTL_DOMAIN, "pesign" }, { "append", 'a', POPT_ARG_VAL|POPT_ARGFLAG_OR, &action, GENERATE_APPEND, "append to variable" }, + {"certdir", 'd', POPT_ARG_STRING|POPT_ARGFLAG_SHOW_DEFAULT, + &certdir, 0, + "specify nss certificate database directory", + "<certificate directory path>" }, { "clear", 'c', POPT_ARG_VAL|POPT_ARGFLAG_OR, &action, GENERATE_CLEAR, "clear variable" }, { "set", 's', POPT_ARG_VAL|POPT_ARGFLAG_OR, &action, @@ -192,6 +306,8 @@ int main(int argc, char *argv[]) "{<namespace>|<guid>}" }, { "name", 'n', POPT_ARG_STRING, &ctx.name, 0, "variable name", "<name>" }, + { "timestamp", 't', POPT_ARG_STRING, &time_str, 0, + "timestamp for the variable", "<time>" }, { "value", 'v', POPT_ARG_STRING, &ctx.value, 0, "value to set or append", "<value>" }, { "valuefile", 'f', POPT_ARG_STRING, &ctx.valuefile, 0, @@ -201,7 +317,7 @@ int main(int argc, char *argv[]) { "export", 'e', POPT_ARG_STRING, &ctx.exportfile, 0, "export variable to <file> instead of firmware", "<file>" }, - { "sign", 'S', POPT_ARG_STRING, &ctx.cms_ctx.certname, 0, + { "sign", 'S', POPT_ARG_STRING, &ctx.cms_ctx->certname, 0, "sign variable with certificate <nickname>", "<nickname>" }, POPT_AUTOALIAS @@ -209,6 +325,25 @@ int main(int argc, char *argv[]) POPT_TABLEEND }; + optCon = poptGetContext("authvar", argc, (const char **)argv, + options, 0); + while ((rc = poptGetNextOpt(optCon)) > 0) + ; + + if (rc < -1) { + fprintf(stderr, "authvar: Invalid argument: %s: %s\n", + poptBadOption(optCon, 0), poptStrerror(rc)); + exit(1); + } + + if (poptPeekArg(optCon)) { + fprintf(stderr, "authvar: Invalid Argument: \"%s\"\n", + poptPeekArg(optCon)); + exit(1); + } + + poptFreeContext(optCon); + if (ctx.importfile) action |= IMPORT; if (ctx.exportfile) @@ -216,14 +351,12 @@ int main(int argc, char *argv[]) if (!(action & (IMPORT|EXPORT))) action |= SET; - if (ctx.cms_ctx.certname && *ctx.cms_ctx.certname) { - rc = find_certificate(&ctx.cms_ctx, 1); - if (rc < 0) { - fprintf(stderr, "authvar: Could not find certificate " - "for \"%s\"\n", ctx.cms_ctx.certname); + if ((action & GENERATE_APPEND) || (action & GENERATE_CLEAR) || + (action & GENERATE_SET)) { + if (!ctx.cms_ctx->certname || !*ctx.cms_ctx->certname) { + fprintf(stderr, "authvar: Require a certificate to sign\n"); exit(1); } - action |= SIGN; } rc = find_namespace_guid(ctxp); @@ -233,25 +366,30 @@ int main(int argc, char *argv[]) exit(1); } - optCon = poptGetContext("authvar", argc, (const char **)argv, - options, 0); - while ((rc = poptGetNextOpt(optCon)) > 0) - ; + set_timestamp(ctxp, time_str); - if (rc < -1) { - fprintf(stderr, "authvar: Invalid argument: %s: %s\n", - poptBadOption(optCon, 0), poptStrerror(rc)); + /* Initialize the NSS db */ + if ((action & GENERATE_APPEND) || (action & GENERATE_CLEAR) || + (action & GENERATE_SET) || (action & SIGN)) + status = NSS_Init(certdir); + else + status = NSS_NoDB_Init(NULL); + if (status != SECSuccess) { + fprintf(stderr, "Could not initialize nss: %s\n", + PORT_ErrorToString(PORT_GetError())); exit(1); } - if (poptPeekArg(optCon)) { - fprintf(stderr, "authvar: Invalid Argument: \"%s\"\n", - poptPeekArg(optCon)); - exit(1); + if (ctx.cms_ctx->certname && *ctx.cms_ctx->certname) { + rc = find_certificate(ctx.cms_ctx, 1); + if (rc < 0) { + fprintf(stderr, "authvar: Could not find certificate " + "for \"%s\"\n", ctx.cms_ctx->certname); + exit(1); + } + action |= SIGN; } - poptFreeContext(optCon); - print_flag_name(stdout, action); printf("\n"); switch (action) { @@ -259,20 +397,48 @@ int main(int argc, char *argv[]) fprintf(stderr, "authvar: No action specified\n"); exit(1); break; - case GENERATE_APPEND|EXPORT: - case GENERATE_APPEND|SET: + case GENERATE_APPEND|EXPORT|SIGN: + case GENERATE_APPEND|SET|SIGN: check_name(ctxp); check_value(ctxp, 1); + open_input(ctxp); + ctxp->attr |= EFI_VARIABLE_APPEND_WRITE; + /* TODO Set Day and Month to 0 */ + + rc = generate_descriptor(ctxp); + if (rc < 0) { + fprintf(stderr, "authvar: unable to generate descriptor\n"); + exit(1); + } + open_output(ctxp); + write_authvar(ctxp); break; - case GENERATE_CLEAR|EXPORT: - case GENERATE_CLEAR|SET: + case GENERATE_CLEAR|EXPORT|SIGN: + case GENERATE_CLEAR|SET|SIGN: check_name(ctxp); check_value(ctxp, 0); + + rc = generate_descriptor(ctxp); + if (rc < 0) { + fprintf(stderr, "authvar: unable to generate descriptor\n"); + exit(1); + } + open_output(ctxp); + write_authvar(ctxp); break; - case GENERATE_SET|EXPORT: - case GENERATE_SET|SET: + case GENERATE_SET|EXPORT|SIGN: + case GENERATE_SET|SET|SIGN: check_name(ctxp); check_value(ctxp, 1); + open_input(ctxp); + + rc = generate_descriptor(ctxp); + if (rc < 0) { + fprintf(stderr, "authvar: unable to generate descriptor\n"); + exit(1); + } + open_output(ctxp); + write_authvar(ctxp); break; case IMPORT|SET: case IMPORT|SIGN|SET: @@ -286,5 +452,10 @@ int main(int argc, char *argv[]) } authvar_context_fini(ctxp); + if (time_str) + xfree(time_str); + + NSS_Shutdown(); + return 0; } diff --git a/src/authvar.h b/src/authvar.h index d8a6b9a..3e7364a 100644 --- a/src/authvar.h +++ b/src/authvar.h @@ -24,6 +24,7 @@ #include "efitypes.h" #include "cms_common.h" +#include "wincert.h" #include "authvar_context.h" #include "util.h" #include "siglist.h" diff --git a/src/authvar_context.c b/src/authvar_context.c index b7230b0..53c6f98 100644 --- a/src/authvar_context.c +++ b/src/authvar_context.c @@ -17,6 +17,14 @@ * Author(s): Peter Jones <pjones@redhat.com> */ +#include <sys/mman.h> + +#include <prerror.h> +#include <nss.h> +#include <pk11pub.h> +#include <secport.h> +#include <secerr.h> + #include "authvar.h" static char *default_namespace="global"; @@ -28,7 +36,12 @@ authvar_context_init(authvar_context *ctx) ctx->namespace = default_namespace; - int rc = cms_context_init(&ctx->cms_ctx); + int rc = cms_context_alloc(&ctx->cms_ctx); + ctx->attr = EFI_VARIABLE_NON_VOLATILE | + EFI_VARIABLE_RUNTIME_ACCESS | + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS; + ctx->exportfd = -1; return rc; } @@ -39,7 +52,136 @@ authvar_context_fini(authvar_context *ctx) if (!ctx) return; - cms_context_fini(&ctx->cms_ctx); + cms_context_fini(ctx->cms_ctx); - memset(ctx, '\0', sizeof (*ctx)); + if (ctx->name) { + xfree(ctx->name); + } + + if (ctx->valuefile) { + munmap(ctx->value, ctx->value_size); + ctx->value = NULL; + + close(ctx->valuefd); + ctx->valuefd = -1; + ctx->value_size = 0; + + xfree(ctx->valuefile); + ctx->valuefile = NULL; + } else if (ctx->value) { + xfree(ctx->value); + ctx->value = NULL; + ctx->value_size = 0; + } + + if (ctx->exportfd >= 0) { + close(ctx->exportfd); + ctx->exportfd = -1; + } +} + +static SECItem* +generate_buffer_digest(cms_context *cms, uint8_t *buf, size_t buf_len) +{ + PK11Context *pk11ctx = NULL; + SECItem *digest = NULL; + + pk11ctx = PK11_CreateDigestContext(SEC_OID_SHA256); + if (!pk11ctx) { + cms->log(cms, LOG_ERR, "%s:%s:%d could not create " + "digest context: %s", + __FILE__, __func__, __LINE__, + PORT_ErrorToString(PORT_GetError())); + goto err; + } + + PK11_DigestBegin(pk11ctx); + PK11_DigestOp(pk11ctx, buf, buf_len); + + digest = PORT_ArenaZAlloc(cms->arena, sizeof (SECItem)); + if (!digest) { + cms->log(cms, LOG_ERR, "%s:%s:%d could not allocate " + "memory: %s", __FILE__, __func__, __LINE__, + PORT_ErrorToString(PORT_GetError())); + goto err; + } + + digest->type = siBuffer; + digest->len = 32; + digest->data = PORT_ArenaZAlloc(cms->arena, 32); + if (!digest->data) { + cms->log(cms, LOG_ERR, "%s:%s:%d could not allocate " + "memory: %s", __FILE__, __func__, __LINE__, + PORT_ErrorToString(PORT_GetError())); + goto err; + } + + PK11_DigestFinal(pk11ctx, digest->data, &digest->len, 32); + PK11_Finalize(pk11ctx); + PK11_DestroyContext(pk11ctx, PR_TRUE); + +err: + return digest; +} + +int +generate_descriptor(authvar_context *ctx) +{ + win_cert_uefi_guid_t *authinfo; + SECItem *digest; + char *name_ptr; + uint8_t *buf, *ptr; + size_t buf_len; + + /* prepare buffer for varname, vendor_guid, attr, timestamp, value */ + buf_len = strlen(ctx->name)*2 + sizeof(efi_guid_t) + sizeof(uint32_t) + + sizeof(efi_time_t) + ctx->value_size; + buf = calloc(1, buf_len); + if (!buf) + return -1; + + ptr = buf; + name_ptr = ctx->name; + while (*name_ptr != '\0') { + ptr++; + *ptr = *name_ptr; + name_ptr++; + } + ptr++; + + memcpy(ptr, &ctx->guid, sizeof(efi_guid_t)); + ptr += sizeof(efi_guid_t); + + memcpy(ptr, &ctx->attr, sizeof(uint32_t)); + ptr += sizeof(uint32_t); + + memcpy(ptr, &ctx->timestamp, sizeof(efi_time_t)); + ptr += sizeof(efi_time_t); + + memcpy(ptr, ctx->value, ctx->value_size); + + digest = generate_buffer_digest(ctx->cms_ctx, buf, buf_len); + if (!digest || !digest->data) { + xfree(buf); + return -1; + } + + /* TODO sign the digest */ + + // TODO complete authinfo + authinfo = &ctx->des.authinfo; + //authinfo->hdr.length + authinfo->hdr.revision = WIN_CERT_REVISION_2_0; + authinfo->hdr.cert_type = WIN_CERT_TYPE_EFI_GUID; + authinfo->type = (efi_guid_t)EFI_CERT_TYPE_PKCS7_GUID; + // TODO append the signed data to authinfo->data + + + return 0; +} + +int +write_authvar(authvar_context *ctx) +{ + return 0; } diff --git a/src/authvar_context.h b/src/authvar_context.h index 88d145d..9647849 100644 --- a/src/authvar_context.h +++ b/src/authvar_context.h @@ -23,15 +23,29 @@ typedef struct { char *namespace; efi_guid_t guid; char *name; - char *value; - char *valuefile; + uint32_t attr; + + char *value; + char *valuefile; + int valuefd; + size_t value_size; + + efi_time_t timestamp; + char *importfile; + int inmportfd; + char *exportfile; + int exportfd; + + efi_var_auth_2_t des; - cms_context cms_ctx; + cms_context *cms_ctx; } authvar_context; extern int authvar_context_init(authvar_context *ctx); extern void authvar_context_fini(authvar_context *ctx); +extern int generate_descriptor(authvar_context *ctx); +extern int write_authvar(authvar_context *ctx); #endif /* AUTHVAR_CONTEXT_H */ diff --git a/src/efitypes.h b/src/efitypes.h index ebd5fef..64683e8 100644 --- a/src/efitypes.h +++ b/src/efitypes.h @@ -50,10 +50,10 @@ typedef struct { } efi_time_t; struct efi_variable { - efi_char16_t VariableName[1024/sizeof(efi_char16_t)]; + efi_char16_t *VariableName; efi_guid_t VendorGuid; unsigned long DataSize; - uint8_t Data[1024]; + uint8_t *Data; efi_status_t Status; uint32_t Attributes; }; diff --git a/src/wincert.h b/src/wincert.h index 77e94b4..bd822d4 100644 --- a/src/wincert.h +++ b/src/wincert.h @@ -19,6 +19,8 @@ #ifndef PESIGN_WINCERT_H #define PESIGN_WINCERT_H 1 +#include "efitypes.h" + #define WIN_CERT_TYPE_PKCS_SIGNED_DATA 0x0002 #define WIN_CERT_TYPE_EFI_OKCS115 0x0EF0 #define WIN_CERT_TYPE_EFI_GUID 0x0EF1 @@ -39,6 +41,17 @@ typedef struct cert_iter { size_t size; } cert_iter; +typedef struct { + win_certificate hdr; + efi_guid_t type; + uint8_t data[1]; +} win_cert_uefi_guid_t; + +typedef struct { + efi_time_t timestamp; + win_cert_uefi_guid_t authinfo; +} efi_var_auth_2_t; + extern int cert_iter_init(cert_iter *iter, Pe *pe); extern int next_cert(cert_iter *iter, void **cert, ssize_t *cert_size); extern ssize_t available_cert_space(Pe *pe); -- 1.8.4.5 From 1a349b52fd37e71226fd01a75298c9b6f3e25277 Mon Sep 17 00:00:00 2001 From: Gary Ching-Pang Lin <glin@suse.com> Date: Tue, 3 Jun 2014 16:38:43 +0800 Subject: [PATCH 16/31] authvar: generate and write the EFI AUTH variable Signed-off-by: Gary Ching-Pang Lin <glin@suse.com> --- src/authvar.c | 18 ++++++++++ src/authvar.h | 2 ++ src/authvar_context.c | 93 +++++++++++++++++++++++++++++++++++++++++---------- src/authvar_context.h | 2 +- 4 files changed, 96 insertions(+), 19 deletions(-) diff --git a/src/authvar.c b/src/authvar.c index cfa9de3..4fb3145 100644 --- a/src/authvar.c +++ b/src/authvar.c @@ -276,6 +276,8 @@ int main(int argc, char *argv[]) authvar_context ctx = { 0, }; authvar_context *ctxp = &ctx; char *time_str = NULL; + char *tokenname = "NSS Certificate DB"; + char *origtoken = tokenname; char *certdir = "/etc/pki/pesign"; SECStatus status; @@ -380,6 +382,22 @@ int main(int argc, char *argv[]) exit(1); } + status = register_oids(ctxp->cms_ctx); + if (status != SECSuccess) { + fprintf(stderr, "Could not register OIDs\n"); + exit(1); + } + + ctxp->cms_ctx->tokenname = tokenname ? + PORT_ArenaStrdup(ctxp->cms_ctx->arena, tokenname) : NULL; + if (tokenname && !ctxp->cms_ctx->tokenname) { + fprintf(stderr, "could not allocate token name: %s\n", + PORT_ErrorToString(PORT_GetError())); + exit(1); + } + if (tokenname != origtoken) + free(tokenname); + if (ctx.cms_ctx->certname && *ctx.cms_ctx->certname) { rc = find_certificate(ctx.cms_ctx, 1); if (rc < 0) { diff --git a/src/authvar.h b/src/authvar.h index 3e7364a..3da5906 100644 --- a/src/authvar.h +++ b/src/authvar.h @@ -31,5 +31,7 @@ #include "endian.h" #include "ucs2.h" #include "varfile.h" +#include "signed_data.h" +#include "oid.h" #endif /* AUTHVAR_H */ diff --git a/src/authvar_context.c b/src/authvar_context.c index 53c6f98..fdc6d7e 100644 --- a/src/authvar_context.c +++ b/src/authvar_context.c @@ -17,6 +17,7 @@ * Author(s): Peter Jones <pjones@redhat.com> */ +#include <unistd.h> #include <sys/mman.h> #include <prerror.h> @@ -80,14 +81,22 @@ authvar_context_fini(authvar_context *ctx) } } -static SECItem* +static int generate_buffer_digest(cms_context *cms, uint8_t *buf, size_t buf_len) { - PK11Context *pk11ctx = NULL; + struct digest *digests = NULL; SECItem *digest = NULL; - pk11ctx = PK11_CreateDigestContext(SEC_OID_SHA256); - if (!pk11ctx) { + if (cms->digests) { + digests = cms->digests; + } else { + digests = PORT_ZAlloc(sizeof (struct digest)); + if (digests == NULL) + cmsreterr(-1, cms, "could not allocate digest context"); + } + + digests[0].pk11ctx = PK11_CreateDigestContext(SEC_OID_SHA256); + if (!digests[0].pk11ctx) { cms->log(cms, LOG_ERR, "%s:%s:%d could not create " "digest context: %s", __FILE__, __func__, __LINE__, @@ -95,8 +104,8 @@ generate_buffer_digest(cms_context *cms, uint8_t *buf, size_t buf_len) goto err; } - PK11_DigestBegin(pk11ctx); - PK11_DigestOp(pk11ctx, buf, buf_len); + PK11_DigestBegin(digests[0].pk11ctx); + PK11_DigestOp(digests[0].pk11ctx, buf, buf_len); digest = PORT_ArenaZAlloc(cms->arena, sizeof (SECItem)); if (!digest) { @@ -116,22 +125,39 @@ generate_buffer_digest(cms_context *cms, uint8_t *buf, size_t buf_len) goto err; } - PK11_DigestFinal(pk11ctx, digest->data, &digest->len, 32); - PK11_Finalize(pk11ctx); - PK11_DestroyContext(pk11ctx, PR_TRUE); + PK11_DigestFinal(digests[0].pk11ctx, digest->data, &digest->len, 32); + PK11_Finalize(digests[0].pk11ctx); + PK11_DestroyContext(digests[0].pk11ctx, PR_TRUE); + cms->digests = digests; + cms->digests[0].pk11ctx = NULL; + /* XXX sure seems like we should be freeing it here, + * but that's segfaulting, and we know it'll get + * cleaned up with PORT_FreeArena a couple of lines + * down. + */ + cms->digests[0].pe_digest = digest; + cms->selected_digest = 0; + + return 0; err: - return digest; + if (digests[0].pk11ctx) + PK11_DestroyContext(digests[0].pk11ctx, PR_TRUE); + + free(digests); + + return -1; } int generate_descriptor(authvar_context *ctx) { win_cert_uefi_guid_t *authinfo; - SECItem *digest; + SECItem sd_der; char *name_ptr; uint8_t *buf, *ptr; size_t buf_len; + int rc; /* prepare buffer for varname, vendor_guid, attr, timestamp, value */ buf_len = strlen(ctx->name)*2 + sizeof(efi_guid_t) + sizeof(uint32_t) + @@ -160,22 +186,28 @@ generate_descriptor(authvar_context *ctx) memcpy(ptr, ctx->value, ctx->value_size); - digest = generate_buffer_digest(ctx->cms_ctx, buf, buf_len); - if (!digest || !digest->data) { + if (generate_buffer_digest(ctx->cms_ctx, buf, buf_len) < 0) { xfree(buf); return -1; } - /* TODO sign the digest */ + /* sign the digest */ + memset(&sd_der, '\0', sizeof(sd_der)); + rc = generate_spc_signed_data(ctx->cms_ctx, &sd_der); + if (rc < 0) + cmsreterr(-1, ctx->cms_ctx, "could not create signed data"); + + authinfo = calloc(sizeof(win_cert_uefi_guid_t) + sd_der.len, 1); + if (!authinfo) + cmsreterr(-1, ctx->cms_ctx, "could not allocate authinfo"); - // TODO complete authinfo - authinfo = &ctx->des.authinfo; - //authinfo->hdr.length + authinfo->hdr.length = sd_der.len + sizeof(win_cert_uefi_guid_t) - 1; authinfo->hdr.revision = WIN_CERT_REVISION_2_0; authinfo->hdr.cert_type = WIN_CERT_TYPE_EFI_GUID; authinfo->type = (efi_guid_t)EFI_CERT_TYPE_PKCS7_GUID; - // TODO append the signed data to authinfo->data + memcpy(&authinfo->data, sd_der.data, sd_der.len); + ctx->authinfo = authinfo; return 0; } @@ -183,5 +215,30 @@ generate_descriptor(authvar_context *ctx) int write_authvar(authvar_context *ctx) { + efi_var_auth_2_t *descriptor; + size_t des_len; + + if (!ctx->authinfo) + cmsreterr(-1, ctx->cms_ctx, "Not a valid authvar"); + + /* The attributes of the variable */ + write(ctx->exportfd, &ctx->attr, sizeof(ctx->attr)); + + /* The EFI_VARIABLE_AUTHENTICATION_2 */ + des_len = sizeof(efi_var_auth_2_t) + ctx->authinfo->hdr.length - + sizeof(win_cert_uefi_guid_t) + 1; + descriptor = calloc(des_len, 1); + if (!descriptor) + cmsreterr(-1, ctx->cms_ctx, "could not allocate descriptor"); + + memcpy(&descriptor->timestamp, &ctx->timestamp, sizeof(efi_time_t)); + memcpy(&descriptor->authinfo, ctx->authinfo, ctx->authinfo->hdr.length); + + write(ctx->exportfd, descriptor, des_len); + + /* The Data */ + if (ctx->value_size > 0) + write(ctx->exportfd, ctx->value, ctx->value_size); + return 0; } diff --git a/src/authvar_context.h b/src/authvar_context.h index 9647849..7e3c696 100644 --- a/src/authvar_context.h +++ b/src/authvar_context.h @@ -38,7 +38,7 @@ typedef struct { char *exportfile; int exportfd; - efi_var_auth_2_t des; + win_cert_uefi_guid_t *authinfo; cms_context *cms_ctx; } authvar_context; -- 1.8.4.5 From 6a5b541d6fc333aa30ec9e80ff82ea4df318e136 Mon Sep 17 00:00:00 2001 From: Gary Ching-Pang Lin <glin@suse.com> Date: Tue, 3 Jun 2014 17:56:57 +0800 Subject: [PATCH 17/31] authvar: collect everything in buffer and write it later Signed-off-by: Gary Ching-Pang Lin <glin@suse.com> --- src/authvar_context.c | 38 ++++++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/src/authvar_context.c b/src/authvar_context.c index fdc6d7e..7bfb0d1 100644 --- a/src/authvar_context.c +++ b/src/authvar_context.c @@ -216,29 +216,43 @@ int write_authvar(authvar_context *ctx) { efi_var_auth_2_t *descriptor; - size_t des_len; + void *buffer, *ptr; + size_t buf_len, des_len, remain; + ssize_t wlen; if (!ctx->authinfo) cmsreterr(-1, ctx->cms_ctx, "Not a valid authvar"); - /* The attributes of the variable */ - write(ctx->exportfd, &ctx->attr, sizeof(ctx->attr)); - - /* The EFI_VARIABLE_AUTHENTICATION_2 */ des_len = sizeof(efi_var_auth_2_t) + ctx->authinfo->hdr.length - sizeof(win_cert_uefi_guid_t) + 1; - descriptor = calloc(des_len, 1); - if (!descriptor) - cmsreterr(-1, ctx->cms_ctx, "could not allocate descriptor"); + buf_len = 4 + des_len + ctx->value_size; + + buffer = calloc(buf_len, 1); + if (!buffer) + cmsreterr(-1, ctx->cms_ctx, "could not allocate buffer"); + ptr = buffer; + /* The attribute of the variable */ + memcpy(ptr, &ctx->attr, sizeof(ctx->attr)); + ptr += sizeof(ctx->attr); + + /* EFI_VARIABLE_AUTHENTICATION_2 */ + descriptor = (efi_var_auth_2_t *)ptr; memcpy(&descriptor->timestamp, &ctx->timestamp, sizeof(efi_time_t)); memcpy(&descriptor->authinfo, ctx->authinfo, ctx->authinfo->hdr.length); + ptr += des_len; - write(ctx->exportfd, descriptor, des_len); - - /* The Data */ + /* Data */ if (ctx->value_size > 0) - write(ctx->exportfd, ctx->value, ctx->value_size); + memcpy(ptr, ctx->value, ctx->value_size); + + remain = buf_len; + do { + wlen = write(ctx->exportfd, buffer, remain); + if (wlen < 0) + cmsreterr(-1, ctx->cms_ctx, "failed to write authvar"); + remain -= wlen; + } while (remain > 0); return 0; } -- 1.8.4.5 From b522876182bf87220da5e40c53e0b38c0f5f14d4 Mon Sep 17 00:00:00 2001 From: Gary Ching-Pang Lin <glin@suse.com> Date: Tue, 3 Jun 2014 18:23:09 +0800 Subject: [PATCH 18/31] authvar: parse the timestamp string Signed-off-by: Gary Ching-Pang Lin <glin@suse.com> --- src/authvar.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/authvar.c b/src/authvar.c index 4fb3145..5923e86 100644 --- a/src/authvar.c +++ b/src/authvar.c @@ -247,21 +247,23 @@ static void set_timestamp(authvar_context *ctx, const char *time_str) { time_t t; - struct tm *tm; + struct tm tm; + memset(&tm, 0, sizeof(struct tm)); if (time_str) { - /* TODO parse the string */ + /* Accept the string like "2001-11-12 18:31:01" */ + strptime(time_str, "%Y-%m-%d %H:%M:%S", &tm); } else { time(&t); - tm = gmtime(&t); + gmtime_r(&t, &tm); } - ctx->timestamp.year = tm->tm_year + 1900; - ctx->timestamp.month = tm->tm_mon + 1; - ctx->timestamp.day = tm->tm_mday; - ctx->timestamp.hour = tm->tm_hour; - ctx->timestamp.minute = tm->tm_min; - ctx->timestamp.second = tm->tm_sec; + ctx->timestamp.year = tm.tm_year + 1900; + ctx->timestamp.month = tm.tm_mon + 1; + ctx->timestamp.day = tm.tm_mday; + ctx->timestamp.hour = tm.tm_hour; + ctx->timestamp.minute = tm.tm_min; + ctx->timestamp.second = tm.tm_sec; ctx->timestamp.pad1 = 0; ctx->timestamp.nanosecond = 0; -- 1.8.4.5 From f376705cefa78845f55d070cf3ac060567636576 Mon Sep 17 00:00:00 2001 From: Gary Ching-Pang Lin <glin@suse.com> Date: Tue, 3 Jun 2014 18:25:22 +0800 Subject: [PATCH 19/31] authvar: adjust timestamp for append Signed-off-by: Gary Ching-Pang Lin <glin@suse.com> --- src/authvar.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/authvar.c b/src/authvar.c index 5923e86..b333139 100644 --- a/src/authvar.c +++ b/src/authvar.c @@ -423,7 +423,8 @@ int main(int argc, char *argv[]) check_value(ctxp, 1); open_input(ctxp); ctxp->attr |= EFI_VARIABLE_APPEND_WRITE; - /* TODO Set Day and Month to 0 */ + ctxp->timestamp.day = 0; + ctxp->timestamp.month = 0; rc = generate_descriptor(ctxp); if (rc < 0) { -- 1.8.4.5 From 9ef7442bbe8f520b61c2397cdabd577401130fbb Mon Sep 17 00:00:00 2001 From: Gary Ching-Pang Lin <glin@suse.com> Date: Thu, 5 Jun 2014 14:50:20 +0800 Subject: [PATCH 20/31] authvar: modify the content of SignedData for authvar Signed-off-by: Gary Ching-Pang Lin <glin@suse.com> --- src/authvar_context.c | 7 +---- src/content_info.c | 28 +++++++++++++++++ src/content_info.h | 1 + src/signed_data.c | 85 ++++++++++++++++++++++++++++++++++++++++++++++++--- src/signed_data.h | 1 + src/signer_info.c | 45 +++++++++++++++++++++++++++ src/signer_info.h | 1 + 7 files changed, 158 insertions(+), 10 deletions(-) diff --git a/src/authvar_context.c b/src/authvar_context.c index 7bfb0d1..95d684c 100644 --- a/src/authvar_context.c +++ b/src/authvar_context.c @@ -131,11 +131,6 @@ generate_buffer_digest(cms_context *cms, uint8_t *buf, size_t buf_len) cms->digests = digests; cms->digests[0].pk11ctx = NULL; - /* XXX sure seems like we should be freeing it here, - * but that's segfaulting, and we know it'll get - * cleaned up with PORT_FreeArena a couple of lines - * down. - */ cms->digests[0].pe_digest = digest; cms->selected_digest = 0; @@ -193,7 +188,7 @@ generate_descriptor(authvar_context *ctx) /* sign the digest */ memset(&sd_der, '\0', sizeof(sd_der)); - rc = generate_spc_signed_data(ctx->cms_ctx, &sd_der); + rc = generate_authvar_signed_data(ctx->cms_ctx, &sd_der); if (rc < 0) cmsreterr(-1, ctx->cms_ctx, "could not create signed data"); diff --git a/src/content_info.c b/src/content_info.c index 1e1fb0e..bae8f8a 100644 --- a/src/content_info.c +++ b/src/content_info.c @@ -388,6 +388,34 @@ generate_spc_content_info(cms_context *cms, SpcContentInfo *cip) return 0; } +int +generate_authvar_content_info(cms_context *cms, SpcContentInfo *cip) +{ + SECOidData *oid; + + if (!cip) + return -1; + + SpcContentInfo ci; + memset(&ci, '\0', sizeof (ci)); + + oid = SECOID_FindOIDByTag(SEC_OID_PKCS7_DATA); + if (oid == NULL) { + cms->log(cms, LOG_ERR, "could not get OID for " + "SEC_OID_PKCS7_DATA"); + return -1; + } + if (SECITEM_CopyItem(cms->arena, &ci.contentType, &oid->oid)) + return -1; + + ci.content.len = 0; + ci.content.data = NULL; + + memcpy(cip, &ci, sizeof *cip); + + return 0; +} + void free_spc_content_info(cms_context *cms, SpcContentInfo *cip) { diff --git a/src/content_info.h b/src/content_info.h index 2c96135..d0cc5a1 100644 --- a/src/content_info.h +++ b/src/content_info.h @@ -35,5 +35,6 @@ extern const SEC_ASN1Template SpcContentInfoTemplate[]; extern int generate_spc_content_info(cms_context *cms, SpcContentInfo *cip); extern void free_spc_content_info(cms_context *cms, SpcContentInfo *cip); extern int register_content_info(void); +extern int generate_authvar_content_info(cms_context *cms, SpcContentInfo *cip); #endif /* CONTENT_INFO_H */ diff --git a/src/signed_data.c b/src/signed_data.c index 2f4b498..2fa1cdd 100644 --- a/src/signed_data.c +++ b/src/signed_data.c @@ -121,11 +121,17 @@ generate_certificate_list(cms_context *cms, SECItem ***certificate_list_p) return 0; } +typedef enum { + PE_SIGNER_INFO, + AUTHVAR_SIGNER_INFO, + END_SIGNER_INFO_LIST +} SignerInfoType; + int -generate_signerInfo_list(cms_context *cms, SpcSignerInfo ***signerInfo_list_p) +generate_signerInfo_list(cms_context *cms, SpcSignerInfo ***signerInfo_list_p, SignerInfoType type) { SpcSignerInfo **signerInfo_list; - int err; + int err, rc; if (!signerInfo_list_p) return -1; @@ -142,7 +148,13 @@ generate_signerInfo_list(cms_context *cms, SpcSignerInfo ***signerInfo_list_p) goto err_list; } - if (generate_spc_signer_info(cms, signerInfo_list[0]) < 0) { + if (type == PE_SIGNER_INFO) + rc = generate_spc_signer_info(cms, signerInfo_list[0]); + else if (type == AUTHVAR_SIGNER_INFO) + rc = generate_authvar_signer_info(cms, signerInfo_list[0]); + else + goto err_item; + if (rc < 0) { err = PORT_GetError(); goto err_item; } @@ -282,7 +294,72 @@ generate_spc_signed_data(cms_context *cms, SECItem *sdp) sd.crls = NULL; - if (generate_signerInfo_list(cms, &sd.signerInfos) < 0) { + if (generate_signerInfo_list(cms, &sd.signerInfos, PE_SIGNER_INFO) < 0) { + PORT_ArenaRelease(cms->arena, mark); + return -1; + } + + SECItem encoded = { 0, }; + if (SEC_ASN1EncodeItem(cms->arena, &encoded, &sd, SignedDataTemplate) + == NULL) { + save_port_err(PORT_ArenaRelease(cms->arena, mark)); + cmsreterr(-1, cms, "could not encode SignedData"); + } + + ContentInfo sdw; + memset(&sdw, '\0', sizeof (sdw)); + + SECOidData *oid = SECOID_FindOIDByTag(SEC_OID_PKCS7_SIGNED_DATA); + + memcpy(&sdw.contentType, &oid->oid, sizeof (sdw.contentType)); + memcpy(&sdw.content, &encoded, sizeof (sdw.content)); + + SECItem wrapper = { 0, }; + if (SEC_ASN1EncodeItem(cms->arena, &wrapper, &sdw, + ContentInfoTemplate) == NULL) { + save_port_err(PORT_ArenaRelease(cms->arena, mark)); + cmsreterr(-1, cms, "could not encode SignedData"); + } + + memcpy(sdp, &wrapper, sizeof(*sdp)); + PORT_ArenaUnmark(cms->arena, mark); + return 0; +} + +int +generate_authvar_signed_data(cms_context *cms, SECItem *sdp) +{ + SignedData sd; + + if (!sdp) + return -1; + + memset(&sd, '\0', sizeof (sd)); + void *mark = PORT_ArenaMark(cms->arena); + + if (SEC_ASN1EncodeInteger(cms->arena, &sd.version, 1) == NULL) { + save_port_err(PORT_ArenaRelease(cms->arena, mark)); + cmsreterr(-1, cms, "could not encode integer"); + } + + if (generate_algorithm_id_list(cms, &sd.algorithms) < 0) { + PORT_ArenaRelease(cms->arena, mark); + return -1; + } + + if (generate_authvar_content_info(cms, &sd.cinfo) < 0) { + PORT_ArenaRelease(cms->arena, mark); + return -1; + } + + if (generate_certificate_list(cms, &sd.certificates) < 0) { + PORT_ArenaRelease(cms->arena, mark); + return -1; + } + + sd.crls = NULL; + + if (generate_signerInfo_list(cms, &sd.signerInfos, AUTHVAR_SIGNER_INFO) < 0) { PORT_ArenaRelease(cms->arena, mark); return -1; } diff --git a/src/signed_data.h b/src/signed_data.h index 7e438fc..645f15e 100644 --- a/src/signed_data.h +++ b/src/signed_data.h @@ -20,5 +20,6 @@ #define SIGNED_DATA_H 1 extern int generate_spc_signed_data(cms_context *cms, SECItem *sdp); +extern int generate_authvar_signed_data(cms_context *cms, SECItem *sdp); #endif /* SIGNED_DATA_H */ diff --git a/src/signer_info.c b/src/signer_info.c index 0a0621e..ef05b7c 100644 --- a/src/signer_info.c +++ b/src/signer_info.c @@ -400,3 +400,48 @@ generate_spc_signer_info(cms_context *cms, SpcSignerInfo *sip) err: return -1; } + +int +generate_authvar_signer_info(cms_context *cms, SpcSignerInfo *sip) +{ + SpcSignerInfo si; + SECItem *authvar_digest; + + if (!sip) + return -1; + + memset(&si, '\0', sizeof (si)); + + if (SEC_ASN1EncodeInteger(cms->arena, &si.CMSVersion, 1) == NULL) { + cms->log(cms, LOG_ERR, "could not encode CMSVersion: %s", + PORT_ErrorToString(PORT_GetError())); + goto err; + } + + si.sid.signerType = signerTypeIssuerAndSerialNumber; + si.sid.signerValue.iasn.issuer = cms->cert->derIssuer; + si.sid.signerValue.iasn.serial = cms->cert->serialNumber; + + if (generate_algorithm_id(cms, &si.digestAlgorithm, + digest_get_digest_oid(cms)) < 0) + goto err; + + si.signedAttrs.len = 0; + si.signedAttrs.data = NULL; + + authvar_digest = cms->digests[0].pe_digest; + if (sign_blob(cms, &si.signature, authvar_digest) < 0) + goto err; + + if (generate_algorithm_id(cms, &si.signatureAlgorithm, + digest_get_encryption_oid(cms)) < 0) + goto err; + + si.unsignedAttrs.len = 0; + si.unsignedAttrs.data = NULL; + + memcpy(sip, &si, sizeof(si)); + return 0; +err: + return -1; +} diff --git a/src/signer_info.h b/src/signer_info.h index f1c9828..724aa7d 100644 --- a/src/signer_info.h +++ b/src/signer_info.h @@ -63,5 +63,6 @@ extern SEC_ASN1Template SpcSignerInfoTemplate[]; extern int generate_signed_attributes(cms_context *cms, SECItem *sattrs); extern int generate_spc_signer_info(cms_context *cms, SpcSignerInfo *sip); +extern int generate_authvar_signer_info(cms_context *cms, SpcSignerInfo *sip); #endif /* SIGNER_INFO */ -- 1.8.4.5 From 7064f04c884fc62bf85b0a03fbc86a078037f03a Mon Sep 17 00:00:00 2001 From: Gary Ching-Pang Lin <glin@suse.com> Date: Mon, 9 Jun 2014 10:30:00 +0800 Subject: [PATCH 21/31] authvar: fix USC2 conversion and the length of the header Also truncate the export file. Signed-off-by: Gary Ching-Pang Lin <glin@suse.com> --- src/authvar_context.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/authvar_context.c b/src/authvar_context.c index 95d684c..8344e82 100644 --- a/src/authvar_context.c +++ b/src/authvar_context.c @@ -152,6 +152,8 @@ generate_descriptor(authvar_context *ctx) char *name_ptr; uint8_t *buf, *ptr; size_t buf_len; + uint64_t offset; + efi_char16_t *wptr; int rc; /* prepare buffer for varname, vendor_guid, attr, timestamp, value */ @@ -164,11 +166,11 @@ generate_descriptor(authvar_context *ctx) ptr = buf; name_ptr = ctx->name; while (*name_ptr != '\0') { - ptr++; - *ptr = *name_ptr; + wptr = (efi_char16_t *)ptr; + *wptr = *name_ptr; name_ptr++; + ptr += sizeof(efi_char16_t); } - ptr++; memcpy(ptr, &ctx->guid, sizeof(efi_guid_t)); ptr += sizeof(efi_guid_t); @@ -192,11 +194,12 @@ generate_descriptor(authvar_context *ctx) if (rc < 0) cmsreterr(-1, ctx->cms_ctx, "could not create signed data"); - authinfo = calloc(sizeof(win_cert_uefi_guid_t) + sd_der.len, 1); + offset = (uint64_t) &((win_cert_uefi_guid_t *)0)->data; + authinfo = calloc(offset + sd_der.len, 1); if (!authinfo) cmsreterr(-1, ctx->cms_ctx, "could not allocate authinfo"); - authinfo->hdr.length = sd_der.len + sizeof(win_cert_uefi_guid_t) - 1; + authinfo->hdr.length = sd_der.len + (uint32_t)offset; authinfo->hdr.revision = WIN_CERT_REVISION_2_0; authinfo->hdr.cert_type = WIN_CERT_TYPE_EFI_GUID; authinfo->type = (efi_guid_t)EFI_CERT_TYPE_PKCS7_GUID; @@ -219,8 +222,8 @@ write_authvar(authvar_context *ctx) cmsreterr(-1, ctx->cms_ctx, "Not a valid authvar"); des_len = sizeof(efi_var_auth_2_t) + ctx->authinfo->hdr.length - - sizeof(win_cert_uefi_guid_t) + 1; - buf_len = 4 + des_len + ctx->value_size; + sizeof(win_cert_uefi_guid_t); + buf_len = sizeof(ctx->attr) + des_len + ctx->value_size; buffer = calloc(buf_len, 1); if (!buffer) @@ -241,6 +244,10 @@ write_authvar(authvar_context *ctx) if (ctx->value_size > 0) memcpy(ptr, ctx->value, ctx->value_size); + /* TODO skip ftruncate while writing a EFI variable in sysfs */ + ftruncate(ctx->exportfd, buf_len); + lseek(ctx->exportfd, 0, SEEK_SET); + remain = buf_len; do { wlen = write(ctx->exportfd, buffer, remain); -- 1.8.4.5 From 9906a3cc8efd133edcc57aeb582b22c92011d7f1 Mon Sep 17 00:00:00 2001 From: Gary Ching-Pang Lin <glin@suse.com> Date: Tue, 10 Jun 2014 12:13:04 +0800 Subject: [PATCH 22/31] authvar: sign the right content We don't have to calculate the digest first. Signed-off-by: Gary Ching-Pang Lin <glin@suse.com> --- src/authvar_context.c | 77 ++++++--------------------------------------------- src/cms_common.c | 5 ++++ src/cms_common.h | 3 ++ src/signer_info.c | 7 +++-- 4 files changed, 20 insertions(+), 72 deletions(-) diff --git a/src/authvar_context.c b/src/authvar_context.c index 8344e82..78bacc4 100644 --- a/src/authvar_context.c +++ b/src/authvar_context.c @@ -81,69 +81,6 @@ authvar_context_fini(authvar_context *ctx) } } -static int -generate_buffer_digest(cms_context *cms, uint8_t *buf, size_t buf_len) -{ - struct digest *digests = NULL; - SECItem *digest = NULL; - - if (cms->digests) { - digests = cms->digests; - } else { - digests = PORT_ZAlloc(sizeof (struct digest)); - if (digests == NULL) - cmsreterr(-1, cms, "could not allocate digest context"); - } - - digests[0].pk11ctx = PK11_CreateDigestContext(SEC_OID_SHA256); - if (!digests[0].pk11ctx) { - cms->log(cms, LOG_ERR, "%s:%s:%d could not create " - "digest context: %s", - __FILE__, __func__, __LINE__, - PORT_ErrorToString(PORT_GetError())); - goto err; - } - - PK11_DigestBegin(digests[0].pk11ctx); - PK11_DigestOp(digests[0].pk11ctx, buf, buf_len); - - digest = PORT_ArenaZAlloc(cms->arena, sizeof (SECItem)); - if (!digest) { - cms->log(cms, LOG_ERR, "%s:%s:%d could not allocate " - "memory: %s", __FILE__, __func__, __LINE__, - PORT_ErrorToString(PORT_GetError())); - goto err; - } - - digest->type = siBuffer; - digest->len = 32; - digest->data = PORT_ArenaZAlloc(cms->arena, 32); - if (!digest->data) { - cms->log(cms, LOG_ERR, "%s:%s:%d could not allocate " - "memory: %s", __FILE__, __func__, __LINE__, - PORT_ErrorToString(PORT_GetError())); - goto err; - } - - PK11_DigestFinal(digests[0].pk11ctx, digest->data, &digest->len, 32); - PK11_Finalize(digests[0].pk11ctx); - PK11_DestroyContext(digests[0].pk11ctx, PR_TRUE); - - cms->digests = digests; - cms->digests[0].pk11ctx = NULL; - cms->digests[0].pe_digest = digest; - cms->selected_digest = 0; - - return 0; -err: - if (digests[0].pk11ctx) - PK11_DestroyContext(digests[0].pk11ctx, PR_TRUE); - - free(digests); - - return -1; -} - int generate_descriptor(authvar_context *ctx) { @@ -157,8 +94,8 @@ generate_descriptor(authvar_context *ctx) int rc; /* prepare buffer for varname, vendor_guid, attr, timestamp, value */ - buf_len = strlen(ctx->name)*2 + sizeof(efi_guid_t) + sizeof(uint32_t) + - sizeof(efi_time_t) + ctx->value_size; + buf_len = strlen(ctx->name)*sizeof(efi_char16_t) + sizeof(efi_guid_t) + + sizeof(uint32_t) + sizeof(efi_time_t) + ctx->value_size; buf = calloc(1, buf_len); if (!buf) return -1; @@ -183,10 +120,12 @@ generate_descriptor(authvar_context *ctx) memcpy(ptr, ctx->value, ctx->value_size); - if (generate_buffer_digest(ctx->cms_ctx, buf, buf_len) < 0) { - xfree(buf); - return -1; - } + ctx->cms_ctx->authbuf_len = buf_len; + ctx->cms_ctx->authbuf = buf; + + /* XXX set the value to get SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION + from digest_get_signature_oid(). */ + ctx->cms_ctx->selected_digest = 0; /* sign the digest */ memset(&sd_der, '\0', sizeof(sd_der)); diff --git a/src/cms_common.c b/src/cms_common.c index 8f035f7..9c99f6a 100644 --- a/src/cms_common.c +++ b/src/cms_common.c @@ -218,6 +218,11 @@ cms_context_fini(cms_context *cms) xfree(cms->signatures); cms->num_signatures = 0; + if (cms->authbuf) { + xfree(cms->authbuf); + cms->authbuf_len = 0; + } + PORT_FreeArena(cms->arena, PR_TRUE); memset(cms, '\0', sizeof(*cms)); xfree(cms); diff --git a/src/cms_common.h b/src/cms_common.h index 019ae40..0d9893f 100644 --- a/src/cms_common.h +++ b/src/cms_common.h @@ -83,6 +83,9 @@ typedef struct cms_context { int num_signatures; SECItem **signatures; + int authbuf_len; + void *authbuf; + cms_common_logger log; void *log_priv; } cms_context; diff --git a/src/signer_info.c b/src/signer_info.c index ef05b7c..afa00e2 100644 --- a/src/signer_info.c +++ b/src/signer_info.c @@ -405,7 +405,7 @@ int generate_authvar_signer_info(cms_context *cms, SpcSignerInfo *sip) { SpcSignerInfo si; - SECItem *authvar_digest; + SECItem buf; if (!sip) return -1; @@ -429,8 +429,9 @@ generate_authvar_signer_info(cms_context *cms, SpcSignerInfo *sip) si.signedAttrs.len = 0; si.signedAttrs.data = NULL; - authvar_digest = cms->digests[0].pe_digest; - if (sign_blob(cms, &si.signature, authvar_digest) < 0) + buf.len = cms->authbuf_len; + buf.data = cms->authbuf; + if (sign_blob(cms, &si.signature, &buf) < 0) goto err; if (generate_algorithm_id(cms, &si.signatureAlgorithm, -- 1.8.4.5 From d69d64cc43c630446eed0e851cf22a4b512780fb Mon Sep 17 00:00:00 2001 From: Gary Ching-Pang Lin <glin@suse.com> Date: Tue, 10 Jun 2014 12:25:07 +0800 Subject: [PATCH 23/31] authvar: don't exit if no value for CLEAR Signed-off-by: Gary Ching-Pang Lin <glin@suse.com> --- src/authvar.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/authvar.c b/src/authvar.c index b333139..4a9fcac 100644 --- a/src/authvar.c +++ b/src/authvar.c @@ -84,8 +84,7 @@ check_value(authvar_context *ctx, int needed) if (needed) fprintf(stderr, "authvar: no value specified.\n"); else - fprintf(stderr, - "authvar: command does not take a value.\n"); + return; exit(1); } if (ctx->value && *ctx->value && ctx->valuefile && *ctx->valuefile) { -- 1.8.4.5 From 301e729061406bd4388febc9737c475f2ff873dc Mon Sep 17 00:00:00 2001 From: Gary Ching-Pang Lin <glin@suse.com> Date: Tue, 10 Jun 2014 12:32:05 +0800 Subject: [PATCH 24/31] authvar: mark "import" as unimplemented Will do it later... Signed-off-by: Gary Ching-Pang Lin <glin@suse.com> --- src/authvar.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/authvar.c b/src/authvar.c index 4a9fcac..dfd40f2 100644 --- a/src/authvar.c +++ b/src/authvar.c @@ -409,8 +409,6 @@ int main(int argc, char *argv[]) action |= SIGN; } - print_flag_name(stdout, action); - printf("\n"); switch (action) { case NO_FLAGS: fprintf(stderr, "authvar: No action specified\n"); @@ -462,7 +460,7 @@ int main(int argc, char *argv[]) break; case IMPORT|SET: case IMPORT|SIGN|SET: - + fprintf(stderr, "authvar: not implemented\n"); case IMPORT|SIGN|EXPORT: default: fprintf(stderr, "authvar: invalid flags: "); -- 1.8.4.5 From c756c108fce07576a67fc4a2719cad7639566604 Mon Sep 17 00:00:00 2001 From: Gary Ching-Pang Lin <glin@suse.com> Date: Tue, 10 Jun 2014 12:48:43 +0800 Subject: [PATCH 25/31] authvar: check the export file Signed-off-by: Gary Ching-Pang Lin <glin@suse.com> --- src/authvar.c | 4 ++++ src/authvar_context.c | 7 ++++--- src/authvar_context.h | 1 + 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/authvar.c b/src/authvar.c index dfd40f2..7e0f54c 100644 --- a/src/authvar.c +++ b/src/authvar.c @@ -156,6 +156,10 @@ open_output(authvar_context *ctx) if (!ctx->exportfile) { generate_efivars_filename(ctx); + ctx->to_firmware = 1; + } else if (access(ctx->exportfile, F_OK) == 0) { + fprintf(stderr, "authvar: \"%s\" exists\n", ctx->exportfile); + exit(1); } flags = O_CREAT|O_RDWR|O_CLOEXEC; diff --git a/src/authvar_context.c b/src/authvar_context.c index 78bacc4..3c82225 100644 --- a/src/authvar_context.c +++ b/src/authvar_context.c @@ -183,9 +183,10 @@ write_authvar(authvar_context *ctx) if (ctx->value_size > 0) memcpy(ptr, ctx->value, ctx->value_size); - /* TODO skip ftruncate while writing a EFI variable in sysfs */ - ftruncate(ctx->exportfd, buf_len); - lseek(ctx->exportfd, 0, SEEK_SET); + if (!ctx->to_firmware) { + ftruncate(ctx->exportfd, buf_len); + lseek(ctx->exportfd, 0, SEEK_SET); + } remain = buf_len; do { diff --git a/src/authvar_context.h b/src/authvar_context.h index 7e3c696..e9250dd 100644 --- a/src/authvar_context.h +++ b/src/authvar_context.h @@ -37,6 +37,7 @@ typedef struct { char *exportfile; int exportfd; + uint8_t to_firmware; win_cert_uefi_guid_t *authinfo; -- 1.8.4.5 From 6ec83a5cb8710082b9761e46e54f52c07edff6a5 Mon Sep 17 00:00:00 2001 From: Gary Ching-Pang Lin <glin@suse.com> Date: Wed, 11 Jun 2014 15:45:03 +0800 Subject: [PATCH 26/31] efisiglist: adjust the signature size I forgot the size of the owner GUID. Signed-off-by: Gary Ching-Pang Lin <glin@suse.com> --- src/siglist.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/siglist.c b/src/siglist.c index e001493..e6a9817 100644 --- a/src/siglist.c +++ b/src/siglist.c @@ -141,7 +141,7 @@ signature_list_add_sig(signature_list *sl, efi_guid_t owner, if (memcmp(&sl->SignatureType, &x509_guid, sizeof (efi_guid_t)) == 0) { if (sigsize > sl->SignatureSize) - resize_entries(sl, sigsize); + resize_entries(sl, sigsize + sizeof (efi_guid_t)); } else if (sigsize != get_sig_type_size(sl->SignatureType)) { fprintf(stderr, "sigsize: %d sl->SignatureSize: %d\n", sigsize, sl->SignatureSize); -- 1.8.4.5 From 6e284c09d1c84900cfcbb237e467544667568a87 Mon Sep 17 00:00:00 2001 From: Gary Ching-Pang Lin <glin@suse.com> Date: Thu, 12 Jun 2014 10:41:50 +0800 Subject: [PATCH 27/31] Install pesigcheck, authvar, and efisiglist Signed-off-by: Gary Ching-Pang Lin <glin@suse.com> --- src/Makefile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index 0aa13a1..9d14d81 100644 --- a/src/Makefile +++ b/src/Makefile @@ -84,7 +84,9 @@ install : $(INSTALL) -m 755 pesign $(INSTALLROOT)$(PREFIX)/bin/ $(INSTALL) -m 755 client $(INSTALLROOT)$(PREFIX)/bin/pesign-client $(INSTALL) -m 755 efikeygen $(INSTALLROOT)$(PREFIX)/bin/ - #$(INSTALL) -m 755 pesigcheck $(INSTALLROOT)$(PREFIX)/bin/ + $(INSTALL) -m 755 pesigcheck $(INSTALLROOT)$(PREFIX)/bin/ + $(INSTALL) -m 755 authvar $(INSTALLROOT)$(PREFIX)/bin/ + $(INSTALL) -m 755 efisiglist $(INSTALLROOT)$(PREFIX)/bin/ $(INSTALL) -d -m 755 $(INSTALLROOT)/etc/popt.d/ $(INSTALL) -m 644 pesign.popt $(INSTALLROOT)/etc/popt.d/ $(INSTALL) -d -m 755 $(INSTALLROOT)/usr/share/man/man1/ -- 1.8.4.5 From afe4aa85503eae83c073c11f8b2fbcb266093726 Mon Sep 17 00:00:00 2001 From: Gary Ching-Pang Lin <glin@suse.com> Date: Wed, 8 Jan 2014 17:41:20 +0800 Subject: [PATCH 28/31] pesigcheck: choose the proper digest algorithm Check the digest algorithm in SignerInfo before calculate/compare the digest Signed-off-by: Gary Ching-Pang Lin <glin@suse.com> --- src/certdb.c | 44 +++++++++++++++++++++++++++++++++++++++++--- src/certdb.h | 2 ++ src/pesigcheck.c | 17 +++++++++++++++-- 3 files changed, 58 insertions(+), 5 deletions(-) diff --git a/src/certdb.c b/src/certdb.c index 24c319b..4239eff 100644 --- a/src/certdb.c +++ b/src/certdb.c @@ -29,6 +29,7 @@ #include <cert.h> #include <pkcs7t.h> #include <pk11pub.h> +#include <sechash.h> #include "pesigcheck.h" @@ -243,6 +244,32 @@ check_db_hash(db_specifier which, pesigcheck_context *ctx) return check_db(which, ctx, check_hash, NULL, 0); } +SECOidTag +find_signer_digest_tag(SEC_PKCS7ContentInfo *cinfo) +{ + SEC_PKCS7SignedData *sdp; + SEC_PKCS7SignerInfo **signerinfos, *signerinfo; + SECOidTag digest_tag; + + sdp = cinfo->content.signedData; + signerinfos = sdp->signerInfos; + + if ((signerinfos == NULL) || (signerinfos[0] == NULL)) { + return SEC_OID_UNKNOWN; + } + + /* The authenticode signature only supports one signer. */ + if (signerinfos[1] != NULL) { + return SEC_OID_UNKNOWN; + } + + signerinfo = signerinfos[0]; + + digest_tag = SECOID_FindOIDTag(&(signerinfo->digestAlg.algorithm)); + + return digest_tag; +} + static db_status check_cert(pesigcheck_context *ctx, SECItem *sig, efi_guid_t *sigtype, SECItem *pkcs7sig) @@ -252,6 +279,9 @@ check_cert(pesigcheck_context *ctx, SECItem *sig, efi_guid_t *sigtype, CERTCertTrust trust; SECItem *content, *digest = NULL; PK11Context *pk11ctx = NULL; + SECOidTag digest_tag; + HASH_HashType hash_type; + uint32_t hash_size; SECOidData *oid; PRBool result; SECStatus rv; @@ -268,13 +298,21 @@ check_cert(pesigcheck_context *ctx, SECItem *sig, efi_guid_t *sigtype, goto out; /* Generate the digest of contentInfo */ - /* XXX support only sha256 for now */ - digest = SECITEM_AllocItem(NULL, NULL, 32); + digest_tag = find_signer_digest_tag(cinfo); + if (digest_tag == SEC_OID_UNKNOWN) + goto out; + + hash_type = HASH_GetHashTypeByOidTag(digest_tag); + if (hash_type == HASH_AlgNULL) + goto out; + + hash_size = HASH_ResultLen(hash_type); + digest = SECITEM_AllocItem(NULL, NULL, hash_size); if (digest == NULL) goto out; content = cinfo->content.signedData->contentInfo.content.data; - oid = SECOID_FindOIDByTag(SEC_OID_SHA256); + oid = SECOID_FindOIDByTag(digest_tag); if (oid == NULL) goto out; pk11ctx = PK11_CreateDigestContext(oid->offset); diff --git a/src/certdb.h b/src/certdb.h index ccf3c87..6aaa58b 100644 --- a/src/certdb.h +++ b/src/certdb.h @@ -50,4 +50,6 @@ extern int add_cert_db(pesigcheck_context *ctx, const char *filename); extern int add_cert_dbx(pesigcheck_context *ctx, const char *filename); extern int add_cert_file(pesigcheck_context *ctx, const char *filename); +extern SECOidTag find_signer_digest_tag(SEC_PKCS7ContentInfo *cinfo); + #endif /* CERTDB_H */ diff --git a/src/pesigcheck.c b/src/pesigcheck.c index 9cf33be..f173121 100644 --- a/src/pesigcheck.c +++ b/src/pesigcheck.c @@ -93,6 +93,7 @@ cert_matches_digest(pesigcheck_context *ctx, void *data, ssize_t datalen) SECItem sig, *pe_digest, *content; uint8_t *digest; SEC_PKCS7ContentInfo *cinfo = NULL; + SECOidTag digest_tag; int ret = -1; sig.data = data; @@ -105,9 +106,21 @@ cert_matches_digest(pesigcheck_context *ctx, void *data, ssize_t datalen) if (!SEC_PKCS7ContentIsSigned(cinfo)) goto out; - /* TODO Find out the digest type in spc_content */ - pe_digest = ctx->cms_ctx->digests[0].pe_digest; + /* The file digest algorithm is the same as the signerinfo digest + algorithm. Utilize the parsed PKC#7 content instead of parsing + SpcContentInfo */ + digest_tag = find_signer_digest_tag(cinfo); + + if (digest_tag == SEC_OID_SHA256) + pe_digest = ctx->cms_ctx->digests[0].pe_digest; + else if (digest_tag == SEC_OID_SHA1) + pe_digest = ctx->cms_ctx->digests[1].pe_digest; + else + goto out; + content = cinfo->content.signedData->contentInfo.content.data; + if (content->len < pe_digest->len) + goto out; digest = content->data + content->len - pe_digest->len; if (memcmp(pe_digest->data, digest, pe_digest->len) != 0) goto out; -- 1.8.4.5 From ef7b38cdb8a1f23cd3cfcbe19835677a9eec2a03 Mon Sep 17 00:00:00 2001 From: Gary Ching-Pang Lin <glin@suse.com> Date: Thu, 12 Jun 2014 11:07:24 +0800 Subject: [PATCH 29/31] make gcc happy --- src/authvar_context.c | 3 ++- src/signed_data.c | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/authvar_context.c b/src/authvar_context.c index 3c82225..5444d3a 100644 --- a/src/authvar_context.c +++ b/src/authvar_context.c @@ -184,7 +184,8 @@ write_authvar(authvar_context *ctx) memcpy(ptr, ctx->value, ctx->value_size); if (!ctx->to_firmware) { - ftruncate(ctx->exportfd, buf_len); + if (ftruncate(ctx->exportfd, buf_len) < 0) + return -1; lseek(ctx->exportfd, 0, SEEK_SET); } diff --git a/src/signed_data.c b/src/signed_data.c index 2fa1cdd..5371a9c 100644 --- a/src/signed_data.c +++ b/src/signed_data.c @@ -133,6 +133,8 @@ generate_signerInfo_list(cms_context *cms, SpcSignerInfo ***signerInfo_list_p, S SpcSignerInfo **signerInfo_list; int err, rc; + err = 0; + if (!signerInfo_list_p) return -1; -- 1.8.4.5 From 741515622a6864668db35318bcb2703d1a8d3883 Mon Sep 17 00:00:00 2001 From: Gary Ching-Pang Lin <glin@suse.com> Date: Thu, 12 Jun 2014 11:20:24 +0800 Subject: [PATCH 30/31] authvar: fix the type cast for 32bit systems Signed-off-by: Gary Ching-Pang Lin <glin@suse.com> --- src/authvar_context.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/authvar_context.c b/src/authvar_context.c index 5444d3a..22e28ce 100644 --- a/src/authvar_context.c +++ b/src/authvar_context.c @@ -133,7 +133,11 @@ generate_descriptor(authvar_context *ctx) if (rc < 0) cmsreterr(-1, ctx->cms_ctx, "could not create signed data"); +#if __WORDSIZE == 64 offset = (uint64_t) &((win_cert_uefi_guid_t *)0)->data; +#else + offset = (uint32_t) &((win_cert_uefi_guid_t *)0)->data; +#endif authinfo = calloc(offset + sd_der.len, 1); if (!authinfo) cmsreterr(-1, ctx->cms_ctx, "could not allocate authinfo"); -- 1.8.4.5 From c72d3e454c8cd5ed4290d7c16027e74f5df3cfe8 Mon Sep 17 00:00:00 2001 From: Gary Ching-Pang Lin <glin@suse.com> Date: Tue, 1 Jul 2014 14:43:35 +0800 Subject: [PATCH 31/31] authvar: fix the write loop I forgot to move the pointer... Signed-off-by: Gary Ching-Pang Lin <glin@suse.com> --- src/authvar_context.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/authvar_context.c b/src/authvar_context.c index 22e28ce..53855f2 100644 --- a/src/authvar_context.c +++ b/src/authvar_context.c @@ -18,6 +18,7 @@ */ #include <unistd.h> +#include <stddef.h> #include <sys/mman.h> #include <prerror.h> @@ -133,11 +134,7 @@ generate_descriptor(authvar_context *ctx) if (rc < 0) cmsreterr(-1, ctx->cms_ctx, "could not create signed data"); -#if __WORDSIZE == 64 - offset = (uint64_t) &((win_cert_uefi_guid_t *)0)->data; -#else - offset = (uint32_t) &((win_cert_uefi_guid_t *)0)->data; -#endif + offset = offsetof(win_cert_uefi_guid_t, data); authinfo = calloc(offset + sd_der.len, 1); if (!authinfo) cmsreterr(-1, ctx->cms_ctx, "could not allocate authinfo"); @@ -160,6 +157,7 @@ write_authvar(authvar_context *ctx) void *buffer, *ptr; size_t buf_len, des_len, remain; ssize_t wlen; + off_t offset; if (!ctx->authinfo) cmsreterr(-1, ctx->cms_ctx, "Not a valid authvar"); @@ -187,18 +185,17 @@ write_authvar(authvar_context *ctx) if (ctx->value_size > 0) memcpy(ptr, ctx->value, ctx->value_size); - if (!ctx->to_firmware) { - if (ftruncate(ctx->exportfd, buf_len) < 0) - return -1; + if (!ctx->to_firmware) lseek(ctx->exportfd, 0, SEEK_SET); - } remain = buf_len; + offset = 0; do { - wlen = write(ctx->exportfd, buffer, remain); + wlen = write(ctx->exportfd, buffer + offset, remain); if (wlen < 0) cmsreterr(-1, ctx->cms_ctx, "failed to write authvar"); remain -= wlen; + offset += wlen; } while (remain > 0); return 0; -- 1.8.4.5
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor