From patchwork Fri May 29 06:41:23 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AKASHI Takahiro X-Patchwork-Id: 1300447 X-Patchwork-Delegate: xypron.glpk@gmx.de Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.denx.de (client-ip=2a01:238:438b:c500:173d:9f52:ddab:ee01; helo=phobos.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dmarc=pass (p=none dis=none) header.from=linaro.org Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=linaro.org header.i=@linaro.org header.a=rsa-sha256 header.s=google header.b=D5L3iHf7; dkim-atps=neutral Received: from phobos.denx.de (phobos.denx.de [IPv6:2a01:238:438b:c500:173d:9f52:ddab:ee01]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 49YFQ56YXHz9sSp for ; Fri, 29 May 2020 16:43:25 +1000 (AEST) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id 8B75581C9E; Fri, 29 May 2020 08:42:44 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=linaro.org Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (2048-bit key; unprotected) header.d=linaro.org header.i=@linaro.org header.b="D5L3iHf7"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id C8C4581C80; Fri, 29 May 2020 08:42:35 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de X-Spam-Level: X-Spam-Status: No, score=-2.0 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,SPF_HELO_NONE,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.2 Received: from mail-pj1-x1044.google.com (mail-pj1-x1044.google.com [IPv6:2607:f8b0:4864:20::1044]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits)) (No client certificate requested) by phobos.denx.de (Postfix) with ESMTPS id 3D07A81B69 for ; Fri, 29 May 2020 08:42:31 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=linaro.org Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=takahiro.akashi@linaro.org Received: by mail-pj1-x1044.google.com with SMTP id q9so763376pjm.2 for ; Thu, 28 May 2020 23:42:31 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=Uy6Q3j0Ku2H/goI4iX5x1dFcfbIx4yGaYniXDIoFZQk=; b=D5L3iHf7c+WNcjWcNQE2pSgUf8VIVQhQbG7aNuEIDA5o6Vqu3U/bhALR6PVsNaQQCp j67J0pI4tVWg0xTKLR4juqcxB/UBose8Kntdb1F9fW7gttC0NhV5zwFA1+8iKFx9myQ3 dvu4ledYi638uTzkbbA1BcPOmOpABpclg0/gYasO4Pp0n0zDi/+kiDz9cC3pNTp9IZjf H8YTE2RcuK7YWgJXu4BYzHZ37cPkl7nEXTOaCwfv7E10fXzqu9dh+6GPIdjYLX0dyclc wkXBKwVL0d1rcB+PEz1dvU8relGM91z2mKA4SmzVIt0bdZIVkU+KUR4Le/kyDUb/Lzvn Itmg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=Uy6Q3j0Ku2H/goI4iX5x1dFcfbIx4yGaYniXDIoFZQk=; b=QmoR9oaHIrMpHTexUkzrHEXgW3k32IGyzBGU4cSiAD0gCydpt9hCSGF30GejF/fAdQ lEbKCcfAAoIoBBPuT5nSOl9b81LQb+lL4nW5yFtwNLaOkFrTZ+xdLcskFdFHxkKIvGf4 08w0ZmXwwYIS36IXAvUrXHzuS0yMFD465ehxbhxbDetGzvCCUomybCjuq3ZsYvevS2L3 QSlWhCoO1rkVf9DKnYsr7VsIAB9/3S43uJNrJ65B+iPSiaNC6qTFu9A/UXvwNbHHqdNZ e3rRjdPRJa+SKFQSgxcCrb8l/h3yE0MynqrgehPpqfUYxUMYOnO/7fFd3vXDi6GauLfu lUmQ== X-Gm-Message-State: AOAM531XK/2iQHvAekWWNOZN/loKcN3pBDpqMT7LJH2Aw+AYofKsPvts ZrVjmNe3NxQTIgjBK87cZwNKjw== X-Google-Smtp-Source: ABdhPJzz4GHmltT1tTH0LAEZVo/1Tdfe7e0GU4MD4bvJiUvXQir4LPj6CivrSvfYnchfpiTb7zyiEA== X-Received: by 2002:a17:90a:2566:: with SMTP id j93mr438542pje.52.1590734549599; Thu, 28 May 2020 23:42:29 -0700 (PDT) Received: from localhost.localdomain (pdb6272d5.tkyea130.ap.so-net.ne.jp. [219.98.114.213]) by smtp.gmail.com with ESMTPSA id q5sm6483128pfl.199.2020.05.28.23.42.26 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 28 May 2020 23:42:28 -0700 (PDT) From: AKASHI Takahiro To: xypron.glpk@gmx.de, agraf@csgraf.de Cc: sughosh.ganu@linaro.org, mail@patrick-wildt.de, u-boot@lists.denx.de, AKASHI Takahiro Subject: [PATCH 06/13] efi_loader: image_loader: verification for all signatures should pass Date: Fri, 29 May 2020 15:41:23 +0900 Message-Id: <20200529064130.28332-7-takahiro.akashi@linaro.org> X-Mailer: git-send-email 2.25.2 In-Reply-To: <20200529064130.28332-1-takahiro.akashi@linaro.org> References: <20200529064130.28332-1-takahiro.akashi@linaro.org> MIME-Version: 1.0 X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.30rc1 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" X-Virus-Scanned: clamav-milter 0.102.2 at phobos.denx.de X-Virus-Status: Clean A signed image may have multiple signatures in - each WIN_CERTIFICATE in authenticode, and/or - each SignerInfo in pkcs7 SignedData (of WIN_CERTIFICATE) In the initial implementation of efi_image_authenticate(), the criteria of verification check for multiple signatures case is a bit ambiguous and it may cause inconsistent result. With this patch, we will make sure that verification check in efi_image_authenticate() should pass against all the signatures. The only exception would be - the case where a digest algorithm used in signature is not supported by U-Boot, or - the case where parsing some portion of authenticode has failed In those cases, we don't know how the signature be handled and should just ignore them. Please note that, due to this change, efi_signature_verify_with_sigdb()'s function prototype will be modified, taking "dbx" as well as "db" instead of outputing a "certificate." If "dbx" is null, the behavior would be the exact same as before. The function's name will be changed to efi_signature_verify() once current efi_signature_verify() has gone due to further improvement in intermedaite certificates support. Signed-off-by: AKASHI Takahiro --- include/efi_loader.h | 10 +- lib/efi_loader/efi_image_loader.c | 37 +++-- lib/efi_loader/efi_signature.c | 266 ++++++++++++++---------------- 3 files changed, 146 insertions(+), 167 deletions(-) diff --git a/include/efi_loader.h b/include/efi_loader.h index 9533df26dc9e..2cbd52e273d4 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -761,14 +761,12 @@ struct efi_signature_store { struct x509_certificate; struct pkcs7_message; -bool efi_signature_verify_cert(struct x509_certificate *cert, - struct efi_signature_store *dbx); -bool efi_signature_verify_signers(struct pkcs7_message *msg, - struct efi_signature_store *dbx); bool efi_signature_verify_with_sigdb(struct efi_image_regions *regs, struct pkcs7_message *msg, - struct efi_signature_store *db, - struct x509_certificate **cert); + struct efi_signature_store *db, + struct efi_signature_store *dbx); +bool efi_signature_check_signers(struct pkcs7_message *msg, + struct efi_signature_store *dbx); efi_status_t efi_image_region_add(struct efi_image_regions *regs, const void *start, const void *end, diff --git a/lib/efi_loader/efi_image_loader.c b/lib/efi_loader/efi_image_loader.c index 5cdaa93519e7..33ffb43f3886 100644 --- a/lib/efi_loader/efi_image_loader.c +++ b/lib/efi_loader/efi_image_loader.c @@ -492,11 +492,12 @@ static bool efi_image_authenticate(void *efi, size_t efi_size) size_t wincerts_len; struct pkcs7_message *msg = NULL; struct efi_signature_store *db = NULL, *dbx = NULL; - struct x509_certificate *cert = NULL; void *new_efi = NULL, *auth, *wincerts_end; size_t new_efi_size, auth_size; bool ret = false; + debug("%s: Enter, %d\n", __func__, ret); + if (!efi_secure_boot_enabled()) return true; @@ -542,7 +543,17 @@ static bool efi_image_authenticate(void *efi, size_t efi_size) goto err; } - /* go through WIN_CERTIFICATE list */ + /* + * go through WIN_CERTIFICATE list + * NOTE: + * We may have multiple signatures either as WIN_CERTIFICATE's + * in PE header, or as pkcs7 SignerInfo's in SignedData. + * So the verification policy here is: + * - Success if, at least, one of signatures is verified + * - unless + * any of signatures is rejected explicitly, or + * none of digest algorithms are supported + */ for (wincert = wincerts, wincerts_end = (void *)wincerts + wincerts_len; (void *)wincert < wincerts_end; wincert = (void *)wincert + ALIGN(wincert->dwLength, 8)) { @@ -596,37 +607,27 @@ static bool efi_image_authenticate(void *efi, size_t efi_size) goto err; } - if (!efi_signature_verify_signers(msg, dbx)) { - debug("Signer was rejected by \"dbx\"\n"); + if (!efi_signature_check_signers(msg, dbx)) { + debug("Signer(s) in \"dbx\"\n"); goto err; - } else { - ret = true; } /* try white-list */ - if (!efi_signature_verify_with_sigdb(regs, msg, db, &cert)) { - debug("Verifying signature with \"db\" failed\n"); + if (!efi_signature_verify_with_sigdb(regs, msg, db, dbx)) { + debug("Signature was not verified by \"db\"\n"); goto err; - } else { - ret = true; - } - - if (!efi_signature_verify_cert(cert, dbx)) { - debug("Certificate was rejected by \"dbx\"\n"); - goto err; - } else { - ret = true; } } + ret = true; err: - x509_free_certificate(cert); efi_sigstore_free(db); efi_sigstore_free(dbx); pkcs7_free_message(msg); free(regs); free(new_efi); + debug("%s: Exit, %d\n", __func__, ret); return ret; } #else diff --git a/lib/efi_loader/efi_signature.c b/lib/efi_loader/efi_signature.c index 00e442783059..ab5687040a38 100644 --- a/lib/efi_loader/efi_signature.c +++ b/lib/efi_loader/efi_signature.c @@ -300,27 +300,111 @@ out: } /** - * efi_signature_verify_with_sigdb - verify a signature with db + * efi_signature_check_revocation - check revocation with dbx + * @sinfo: Signer's info + * @cert: x509 certificate + * @dbx: Revocation signature database + * + * Search revocation signature database pointed to by @dbx and find + * an entry matching to certificate pointed to by @cert. + * + * While this entry contains revocation time, we don't support timestamp + * protocol at this time and any image will be unconditionally revoked + * when this match occurs. + * + * Return: true if check passed, false otherwise. + */ +static bool efi_signature_check_revocation(struct pkcs7_signed_info *sinfo, + struct x509_certificate *cert, + struct efi_signature_store *dbx) +{ + struct efi_signature_store *siglist; + struct efi_sig_data *sig_data; + struct image_region reg[1]; + void *hash = NULL; + size_t size = 0; + time64_t revoc_time; + bool revoked = false; + + debug("%s: Enter, %p, %p, %p\n", __func__, sinfo, cert, dbx); + + if (!sinfo || !cert || !dbx || !dbx->sig_data_list) + goto out; + + debug("Checking revocation against %s\n", cert->subject); + for (siglist = dbx; siglist; siglist = siglist->next) { + if (guidcmp(&siglist->sig_type, &efi_guid_cert_x509_sha256)) + continue; + + /* calculate hash of TBSCertificate */ + reg[0].data = cert->tbs; + reg[0].size = cert->tbs_size; + if (!efi_hash_regions(reg, 1, &hash, &size)) + goto out; + + for (sig_data = siglist->sig_data_list; sig_data; + sig_data = sig_data->next) { + /* + * struct efi_cert_x509_sha256 { + * u8 tbs_hash[256/8]; + * time64_t revocation_time; + * }; + */ +#ifdef DEBUG + if (sig_data->size >= size) { + debug("hash in db:\n"); + print_hex_dump(" ", DUMP_PREFIX_OFFSET, + 16, 1, + sig_data->data, size, false); + } +#endif + if ((sig_data->size < size + sizeof(time64_t)) || + memcmp(sig_data->data, hash, size)) + continue; + + memcpy(&revoc_time, sig_data->data + size, + sizeof(revoc_time)); + debug("revocation time: 0x%llx\n", revoc_time); + /* + * TODO: compare signing timestamp in sinfo + * with revocation time + */ + + revoked = true; + free(hash); + goto out; + } + free(hash); + hash = NULL; + } +out: + debug("%s: Exit, revoked: %d\n", __func__, revoked); + return !revoked; +} + +/** + * efi_signature_verify_with_sigdb - verify signatures with db and dbx * @regs: List of regions to be authenticated * @msg: Signature * @db: Signature database for trusted certificates - * @cert: x509 certificate that verifies this signature + * @dbx: Revocation signature database * - * Signature pointed to by @msg against image pointed to by @regs - * is verified by signature database pointed to by @db. + * All the signature pointed to by @msg against image pointed to by @regs + * will be verified by signature database pointed to by @db and @dbx. * - * Return: true if signature is verified, false if not + * Return: true if verification for all signatures passed, false otherwise */ bool efi_signature_verify_with_sigdb(struct efi_image_regions *regs, struct pkcs7_message *msg, struct efi_signature_store *db, - struct x509_certificate **cert) + struct efi_signature_store *dbx) { struct pkcs7_signed_info *info; struct efi_signature_store *siglist; + struct x509_certificate *cert; bool verified = false; - debug("%s: Enter, %p, %p, %p, %p\n", __func__, regs, msg, db, cert); + debug("%s: Enter, %p, %p, %p, %p\n", __func__, regs, msg, db, dbx); if (!db) goto out; @@ -333,7 +417,7 @@ bool efi_signature_verify_with_sigdb(struct efi_image_regions *regs, debug("%s: Verify unsigned image with db\n", __func__); for (siglist = db; siglist; siglist = siglist->next) if (efi_signature_verify_with_list(regs, NULL, NULL, - siglist, cert)) { + siglist, &cert)) { verified = true; goto out; } @@ -349,12 +433,21 @@ bool efi_signature_verify_with_sigdb(struct efi_image_regions *regs, for (siglist = db; siglist; siglist = siglist->next) { if (efi_signature_verify_with_list(regs, msg, info, - siglist, cert)) { - verified = true; - goto out; - } + siglist, &cert)) + break; } + if (!siglist) { + debug("No valid certificate in \"db\"\n"); + goto out; + } + + if (!dbx || efi_signature_check_revocation(info, cert, dbx)) + continue; + + debug("Certificate in \"dbx\"\n"); + goto out; } + verified = true; out: debug("%s: Exit, verified: %d\n", __func__, verified); @@ -362,150 +455,37 @@ out: } /** - * efi_search_siglist - search signature list for a certificate - * @cert: x509 certificate - * @siglist: Signature list - * @revoc_time: Pointer to buffer for revocation time - * - * Search signature list pointed to by @siglist and find a certificate - * pointed to by @cert. - * If found, revocation time that is specified in signature database is - * returned in @revoc_time. - * - * Return: true if certificate is found, false if not - */ -static bool efi_search_siglist(struct x509_certificate *cert, - struct efi_signature_store *siglist, - time64_t *revoc_time) -{ - struct image_region reg[1]; - void *hash = NULL, *msg = NULL; - struct efi_sig_data *sig_data; - bool found = false; - - /* can be null */ - if (!siglist->sig_data_list) - return false; - - if (guidcmp(&siglist->sig_type, &efi_guid_cert_x509_sha256)) { - /* TODO: other hash algos */ - debug("Certificate's digest type is not supported: %pUl\n", - &siglist->sig_type); - goto out; - } - - /* calculate hash of TBSCertificate */ - msg = calloc(1, SHA256_SUM_LEN); - if (!msg) { - debug("Out of memory\n"); - goto out; - } - - hash = calloc(1, SHA256_SUM_LEN); - if (!hash) { - debug("Out of memory\n"); - goto out; - } - - reg[0].data = cert->tbs; - reg[0].size = cert->tbs_size; - hash_calculate("sha256", reg, 1, msg); - - /* go through signature list */ - for (sig_data = siglist->sig_data_list; sig_data; - sig_data = sig_data->next) { - /* - * struct efi_cert_x509_sha256 { - * u8 tbs_hash[256/8]; - * time64_t revocation_time; - * }; - */ - if ((sig_data->size >= SHA256_SUM_LEN + sizeof(time64_t)) && - !memcmp(sig_data->data, msg, SHA256_SUM_LEN)) { - memcpy(revoc_time, sig_data->data + SHA256_SUM_LEN, - sizeof(*revoc_time)); - debug("revocation time: 0x%llx\n", *revoc_time); - found = true; - goto out; - } - } - -out: - free(hash); - free(msg); - - return found; -} - -/** - * efi_signature_verify_cert - verify a certificate with dbx - * @cert: x509 certificate - * @dbx: Signature database - * - * Search signature database pointed to by @dbx and find a certificate - * pointed to by @cert. - * This function is expected to be used against "dbx". - * - * Return: true if a certificate is not rejected, false otherwise. - */ -bool efi_signature_verify_cert(struct x509_certificate *cert, - struct efi_signature_store *dbx) -{ - struct efi_signature_store *siglist; - time64_t revoc_time; - bool found = false; - - debug("%s: Enter, %p, %p\n", __func__, dbx, cert); - - if (!cert) - return false; - - for (siglist = dbx; siglist; siglist = siglist->next) { - if (efi_search_siglist(cert, siglist, &revoc_time)) { - /* TODO */ - /* compare signing time with revocation time */ - - found = true; - break; - } - } - - debug("%s: Exit, verified: %d\n", __func__, !found); - return !found; -} - -/** - * efi_signature_verify_signers - verify signers' certificates with dbx + * efi_signature_check_signers - check revocation against all signers with dbx * @msg: Signature - * @dbx: Signature database + * @dbx: Revocation signature database * - * Determine if any of signers' certificates in @msg may be verified - * by any of certificates in signature database pointed to by @dbx. - * This function is expected to be used against "dbx". + * Determine if none of signers' certificates in @msg are revoked + * by signature database pointed to by @dbx. * - * Return: true if none of certificates is rejected, false otherwise. + * Return: true if all signers passed, false otherwise. */ -bool efi_signature_verify_signers(struct pkcs7_message *msg, - struct efi_signature_store *dbx) +bool efi_signature_check_signers(struct pkcs7_message *msg, + struct efi_signature_store *dbx) { - struct pkcs7_signed_info *info; - bool found = false; + struct pkcs7_signed_info *sinfo; + bool revoked = false; debug("%s: Enter, %p, %p\n", __func__, msg, dbx); - if (!msg) + if (!msg || !dbx) goto out; - for (info = msg->signed_infos; info; info = info->next) { - if (info->signer && - !efi_signature_verify_cert(info->signer, dbx)) { - found = true; - goto out; + for (sinfo = msg->signed_infos; sinfo; sinfo = sinfo->next) { + if (sinfo->signer && + !efi_signature_check_revocation(sinfo, sinfo->signer, + dbx)) { + revoked = true; + break; } } out: - debug("%s: Exit, verified: %d\n", __func__, !found); - return !found; + debug("%s: Exit, revoked: %d\n", __func__, revoked); + return !revoked; } /**