1 Star 0 Fork 16

Sevever/grub2_oc

forked from OpenCloudOS Stream/grub2 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
0111-appended-signatures-parse-PKCS-7-signedData-and-X.50.patch 50.62 KB
一键复制 编辑 原始数据 按行查看 历史
nilusyi 提交于 2024-04-07 16:45 . update patches
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871
From c09a70f26d0260e863fc9d3e4763f44df1208714 Mon Sep 17 00:00:00 2001
From: Daniel Axtens <[email protected]>
Date: Thu, 30 Jul 2020 01:33:46 +1000
Subject: [PATCH 111/272] appended signatures: parse PKCS#7 signedData and
X.509 certificates
This code allows us to parse:
- PKCS#7 signedData messages. Only a single signerInfo is supported,
which is all that the Linux sign-file utility supports creating
out-of-the-box. Only RSA, SHA-256 and SHA-512 are supported.
Any certificate embedded in the PKCS#7 message will be ignored.
- X.509 certificates: at least enough to verify the signatures on the
PKCS#7 messages. We expect that the certificates embedded in grub will
be leaf certificates, not CA certificates. The parser enforces this.
- X.509 certificates support the Extended Key Usage extension and handle
it by verifying that the certificate has a single purpose, that is code
signing. This is required because Red Hat certificates have both Key
Usage and Extended Key Usage extensions present.
Signed-off-by: Javier Martinez Canillas <[email protected]> # EKU support
Signed-off-by: Daniel Axtens <[email protected]>
---
grub-core/commands/appendedsig/appendedsig.h | 118 ++
grub-core/commands/appendedsig/asn1util.c | 103 ++
grub-core/commands/appendedsig/pkcs7.c | 509 +++++++++
grub-core/commands/appendedsig/x509.c | 1079 ++++++++++++++++++
4 files changed, 1809 insertions(+)
create mode 100644 grub-core/commands/appendedsig/appendedsig.h
create mode 100644 grub-core/commands/appendedsig/asn1util.c
create mode 100644 grub-core/commands/appendedsig/pkcs7.c
create mode 100644 grub-core/commands/appendedsig/x509.c
diff --git a/grub-core/commands/appendedsig/appendedsig.h b/grub-core/commands/appendedsig/appendedsig.h
new file mode 100644
index 000000000..327d68ddb
--- /dev/null
+++ b/grub-core/commands/appendedsig/appendedsig.h
@@ -0,0 +1,118 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2020 IBM Corporation.
+ *
+ * GRUB 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, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB 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 GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/crypto.h>
+#include <grub/libtasn1.h>
+
+extern asn1_node _gnutls_gnutls_asn;
+extern asn1_node _gnutls_pkix_asn;
+
+#define MAX_OID_LEN 32
+
+/*
+ * One or more x509 certificates.
+ *
+ * We do limited parsing: extracting only the serial, CN and RSA public key.
+ */
+struct x509_certificate
+{
+ struct x509_certificate *next;
+
+ grub_uint8_t *serial;
+ grub_size_t serial_len;
+
+ char *subject;
+ grub_size_t subject_len;
+
+ /* We only support RSA public keys. This encodes [modulus, publicExponent] */
+ gcry_mpi_t mpis[2];
+};
+
+/*
+ * A PKCS#7 signedData signerInfo.
+ */
+struct pkcs7_signerInfo
+{
+ const gcry_md_spec_t *hash;
+ gcry_mpi_t sig_mpi;
+};
+
+/*
+ * A PKCS#7 signedData message.
+ *
+ * We make no attempt to match intelligently, so we don't save any info about
+ * the signer.
+ */
+struct pkcs7_signedData
+{
+ int signerInfo_count;
+ struct pkcs7_signerInfo *signerInfos;
+};
+
+
+/* Do libtasn1 init */
+int asn1_init (void);
+
+/*
+ * Import a DER-encoded certificate at 'data', of size 'size'.
+ *
+ * Place the results into 'results', which must be already allocated.
+ */
+grub_err_t
+parse_x509_certificate (const void *data, grub_size_t size,
+ struct x509_certificate *results);
+
+/*
+ * Release all the storage associated with the x509 certificate.
+ * If the caller dynamically allocated the certificate, it must free it.
+ * The caller is also responsible for maintenance of the linked list.
+ */
+void certificate_release (struct x509_certificate *cert);
+
+/*
+ * Parse a PKCS#7 message, which must be a signedData message.
+ *
+ * The message must be in 'sigbuf' and of size 'data_size'. The result is
+ * placed in 'msg', which must already be allocated.
+ */
+grub_err_t
+parse_pkcs7_signedData (const void *sigbuf, grub_size_t data_size,
+ struct pkcs7_signedData *msg);
+
+/*
+ * Release all the storage associated with the PKCS#7 message.
+ * If the caller dynamically allocated the message, it must free it.
+ */
+void pkcs7_signedData_release (struct pkcs7_signedData *msg);
+
+/*
+ * Read a value from an ASN1 node, allocating memory to store it.
+ *
+ * It will work for anything where the size libtasn1 returns is right:
+ * - Integers
+ * - Octet strings
+ * - DER encoding of other structures
+ * It will _not_ work for things where libtasn1 size requires adjustment:
+ * - Strings that require an extra NULL byte at the end
+ * - Bit strings because libtasn1 returns the length in bits, not bytes.
+ *
+ * If the function returns a non-NULL value, the caller must free it.
+ */
+void *grub_asn1_allocate_and_read (asn1_node node, const char *name,
+ const char *friendly_name,
+ int *content_size);
diff --git a/grub-core/commands/appendedsig/asn1util.c b/grub-core/commands/appendedsig/asn1util.c
new file mode 100644
index 000000000..e116f8c75
--- /dev/null
+++ b/grub-core/commands/appendedsig/asn1util.c
@@ -0,0 +1,103 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2020 IBM Corporation.
+ *
+ * GRUB 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, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB 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 GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/libtasn1.h>
+#include <grub/types.h>
+#include <grub/err.h>
+#include <grub/mm.h>
+#include <grub/crypto.h>
+#include <grub/misc.h>
+#include <grub/gcrypt/gcrypt.h>
+
+#include "appendedsig.h"
+
+asn1_node _gnutls_gnutls_asn = NULL;
+asn1_node _gnutls_pkix_asn = NULL;
+
+extern const asn1_static_node gnutls_asn1_tab[];
+extern const asn1_static_node pkix_asn1_tab[];
+
+/*
+ * Read a value from an ASN1 node, allocating memory to store it.
+ *
+ * It will work for anything where the size libtasn1 returns is right:
+ * - Integers
+ * - Octet strings
+ * - DER encoding of other structures
+ * It will _not_ work for things where libtasn1 size requires adjustment:
+ * - Strings that require an extra NULL byte at the end
+ * - Bit strings because libtasn1 returns the length in bits, not bytes.
+ *
+ * If the function returns a non-NULL value, the caller must free it.
+ */
+void *
+grub_asn1_allocate_and_read (asn1_node node, const char *name,
+ const char *friendly_name, int *content_size)
+{
+ int result;
+ grub_uint8_t *tmpstr = NULL;
+ int tmpstr_size = 0;
+
+ result = asn1_read_value (node, name, NULL, &tmpstr_size);
+ if (result != ASN1_MEM_ERROR)
+ {
+ grub_snprintf (grub_errmsg, sizeof (grub_errmsg),
+ _
+ ("Reading size of %s did not return expected status: %s"),
+ friendly_name, asn1_strerror (result));
+ grub_errno = GRUB_ERR_BAD_FILE_TYPE;
+ return NULL;
+ }
+
+ tmpstr = grub_malloc (tmpstr_size);
+ if (tmpstr == NULL)
+ {
+ grub_snprintf (grub_errmsg, sizeof (grub_errmsg),
+ "Could not allocate memory to store %s", friendly_name);
+ grub_errno = GRUB_ERR_OUT_OF_MEMORY;
+ return NULL;
+ }
+
+ result = asn1_read_value (node, name, tmpstr, &tmpstr_size);
+ if (result != ASN1_SUCCESS)
+ {
+ grub_free (tmpstr);
+ grub_snprintf (grub_errmsg, sizeof (grub_errmsg),
+ "Error reading %s: %s",
+ friendly_name, asn1_strerror (result));
+ grub_errno = GRUB_ERR_BAD_FILE_TYPE;
+ return NULL;
+ }
+
+ *content_size = tmpstr_size;
+
+ return tmpstr;
+}
+
+int
+asn1_init (void)
+{
+ int res;
+ res = asn1_array2tree (gnutls_asn1_tab, &_gnutls_gnutls_asn, NULL);
+ if (res != ASN1_SUCCESS)
+ {
+ return res;
+ }
+ res = asn1_array2tree (pkix_asn1_tab, &_gnutls_pkix_asn, NULL);
+ return res;
+}
diff --git a/grub-core/commands/appendedsig/pkcs7.c b/grub-core/commands/appendedsig/pkcs7.c
new file mode 100644
index 000000000..845f58a53
--- /dev/null
+++ b/grub-core/commands/appendedsig/pkcs7.c
@@ -0,0 +1,509 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2020 IBM Corporation.
+ *
+ * GRUB 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, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB 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 GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "appendedsig.h"
+#include <grub/misc.h>
+#include <grub/crypto.h>
+#include <grub/gcrypt/gcrypt.h>
+#include <sys/types.h>
+
+static char asn1_error[ASN1_MAX_ERROR_DESCRIPTION_SIZE];
+
+/*
+ * RFC 5652 s 5.1
+ */
+const char *signedData_oid = "1.2.840.113549.1.7.2";
+
+/*
+ * RFC 4055 s 2.1
+ */
+const char *sha256_oid = "2.16.840.1.101.3.4.2.1";
+const char *sha512_oid = "2.16.840.1.101.3.4.2.3";
+
+static grub_err_t
+process_content (grub_uint8_t * content, int size,
+ struct pkcs7_signedData *msg)
+{
+ int res;
+ asn1_node signed_part;
+ grub_err_t err = GRUB_ERR_NONE;
+ char algo_oid[MAX_OID_LEN];
+ int algo_oid_size = sizeof (algo_oid);
+ int algo_count;
+ int signer_count;
+ int i;
+ char version;
+ int version_size = sizeof (version);
+ grub_uint8_t *result_buf;
+ int result_size = 0;
+ int crls_size = 0;
+ gcry_error_t gcry_err;
+ bool sha256_in_da, sha256_in_si, sha512_in_da, sha512_in_si;
+ char *da_path;
+ char *si_sig_path;
+ char *si_da_path;
+
+ res = asn1_create_element (_gnutls_pkix_asn, "PKIX1.pkcs-7-SignedData",
+ &signed_part);
+ if (res != ASN1_SUCCESS)
+ {
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "Could not create ASN.1 structure for PKCS#7 signed part.");
+ }
+
+ res = asn1_der_decoding2 (&signed_part, content, &size,
+ ASN1_DECODE_FLAG_STRICT_DER, asn1_error);
+ if (res != ASN1_SUCCESS)
+ {
+ err =
+ grub_error (GRUB_ERR_BAD_SIGNATURE,
+ "Error reading PKCS#7 signed data: %s", asn1_error);
+ goto cleanup_signed_part;
+ }
+
+ /* SignedData ::= SEQUENCE {
+ * version CMSVersion,
+ * digestAlgorithms DigestAlgorithmIdentifiers,
+ * encapContentInfo EncapsulatedContentInfo,
+ * certificates [0] IMPLICIT CertificateSet OPTIONAL,
+ * crls [1] IMPLICIT RevocationInfoChoices OPTIONAL,
+ * signerInfos SignerInfos }
+ */
+
+ /* version per the algo in 5.1, must be 1 */
+ res = asn1_read_value (signed_part, "version", &version, &version_size);
+ if (res != ASN1_SUCCESS)
+ {
+ err =
+ grub_error (GRUB_ERR_BAD_SIGNATURE,
+ "Error reading signedData version: %s",
+ asn1_strerror (res));
+ goto cleanup_signed_part;
+ }
+
+ if (version != 1)
+ {
+ err =
+ grub_error (GRUB_ERR_BAD_SIGNATURE,
+ "Unexpected signature version v%d, only v1 supported",
+ version);
+ goto cleanup_signed_part;
+ }
+
+ /*
+ * digestAlgorithms DigestAlgorithmIdentifiers
+ *
+ * DigestAlgorithmIdentifiers ::= SET OF DigestAlgorithmIdentifier
+ * DigestAlgorithmIdentifer is an X.509 AlgorithmIdentifier (10.1.1)
+ *
+ * RFC 4055 s 2.1:
+ * sha256Identifier AlgorithmIdentifier ::= { id-sha256, NULL }
+ * sha512Identifier AlgorithmIdentifier ::= { id-sha512, NULL }
+ *
+ * We only support 1 element in the set, and we do not check parameters atm.
+ */
+ res =
+ asn1_number_of_elements (signed_part, "digestAlgorithms", &algo_count);
+ if (res != ASN1_SUCCESS)
+ {
+ err =
+ grub_error (GRUB_ERR_BAD_SIGNATURE,
+ "Error counting number of digest algorithms: %s",
+ asn1_strerror (res));
+ goto cleanup_signed_part;
+ }
+
+ if (algo_count <= 0)
+ {
+ err =
+ grub_error (GRUB_ERR_BAD_SIGNATURE,
+ "A minimum of 1 digest algorithm is required");
+ goto cleanup_signed_part;
+ }
+
+ if (algo_count > 2)
+ {
+ err =
+ grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
+ "A maximum of 2 digest algorithms are supported");
+ goto cleanup_signed_part;
+ }
+
+ sha256_in_da = false;
+ sha512_in_da = false;
+
+ for (i = 0; i < algo_count; i++)
+ {
+ da_path = grub_xasprintf ("digestAlgorithms.?%d.algorithm", i + 1);
+ if (!da_path)
+ {
+ err = grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "Could not allocate path for digest algorithm parsing path");
+ goto cleanup_signed_part;
+ }
+
+ algo_oid_size = sizeof (algo_oid);
+ res = asn1_read_value (signed_part, da_path, algo_oid, &algo_oid_size);
+ if (res != ASN1_SUCCESS)
+ {
+ err =
+ grub_error (GRUB_ERR_BAD_SIGNATURE,
+ "Error reading digest algorithm: %s",
+ asn1_strerror (res));
+ grub_free (da_path);
+ goto cleanup_signed_part;
+ }
+
+ if (grub_strncmp (sha512_oid, algo_oid, algo_oid_size) == 0)
+ {
+ if (!sha512_in_da)
+ {
+ sha512_in_da = true;
+ }
+ else
+ {
+ err =
+ grub_error (GRUB_ERR_BAD_SIGNATURE,
+ "SHA-512 specified twice in digest algorithm list");
+ grub_free (da_path);
+ goto cleanup_signed_part;
+ }
+ }
+ else if (grub_strncmp (sha256_oid, algo_oid, algo_oid_size) == 0)
+ {
+ if (!sha256_in_da)
+ {
+ sha256_in_da = true;
+ }
+ else
+ {
+ err =
+ grub_error (GRUB_ERR_BAD_SIGNATURE,
+ "SHA-256 specified twice in digest algorithm list");
+ grub_free (da_path);
+ goto cleanup_signed_part;
+ }
+ }
+ else
+ {
+ err =
+ grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
+ "Only SHA-256 and SHA-512 hashes are supported, found OID %s",
+ algo_oid);
+ grub_free (da_path);
+ goto cleanup_signed_part;
+ }
+
+ grub_free (da_path);
+ }
+
+ /* at this point, at least one of sha{256,512}_in_da must be true */
+
+ /*
+ * We ignore the certificates, but we don't permit CRLs.
+ * A CRL entry might be revoking the certificate we're using, and we have
+ * no way of dealing with that at the moment.
+ */
+ res = asn1_read_value (signed_part, "crls", NULL, &crls_size);
+ if (res != ASN1_ELEMENT_NOT_FOUND)
+ {
+ err =
+ grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
+ "PKCS#7 messages with embedded CRLs are not supported");
+ goto cleanup_signed_part;
+ }
+
+ /* read the signatures */
+
+ res = asn1_number_of_elements (signed_part, "signerInfos", &signer_count);
+ if (res != ASN1_SUCCESS)
+ {
+ err =
+ grub_error (GRUB_ERR_BAD_SIGNATURE,
+ "Error counting number of signers: %s",
+ asn1_strerror (res));
+ goto cleanup_signed_part;
+ }
+
+ if (signer_count <= 0)
+ {
+ err =
+ grub_error (GRUB_ERR_BAD_SIGNATURE,
+ "A minimum of 1 signer is required");
+ goto cleanup_signed_part;
+ }
+
+ msg->signerInfos = grub_calloc (signer_count,
+ sizeof (struct pkcs7_signerInfo));
+ if (!msg->signerInfos)
+ {
+ err = grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "Could not allocate space for %d signers",
+ signer_count);
+ goto cleanup_signed_part;
+ }
+
+ msg->signerInfo_count = 0;
+ for (i = 0; i < signer_count; i++)
+ {
+ si_da_path =
+ grub_xasprintf ("signerInfos.?%d.digestAlgorithm.algorithm", i + 1);
+ if (!si_da_path)
+ {
+ err = grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "Could not allocate path for signer %d's digest algorithm parsing path",
+ i);
+ goto cleanup_signerInfos;
+ }
+
+ algo_oid_size = sizeof (algo_oid);
+ res =
+ asn1_read_value (signed_part, si_da_path, algo_oid, &algo_oid_size);
+ if (res != ASN1_SUCCESS)
+ {
+ err =
+ grub_error (GRUB_ERR_BAD_SIGNATURE,
+ "Error reading signer %d's digest algorithm: %s",
+ i, asn1_strerror (res));
+ grub_free (si_da_path);
+ goto cleanup_signerInfos;
+ }
+
+ grub_free (si_da_path);
+
+ if (grub_strncmp (sha512_oid, algo_oid, algo_oid_size) == 0)
+ {
+ if (!sha512_in_da)
+ {
+ err =
+ grub_error (GRUB_ERR_BAD_SIGNATURE,
+ "Signer %d claims a SHA-512 signature which was not specified in the outer DigestAlgorithms",
+ i);
+ goto cleanup_signerInfos;
+ }
+ else
+ {
+ sha512_in_si = true;
+ msg->signerInfos[i].hash =
+ grub_crypto_lookup_md_by_name ("sha512");
+ }
+ }
+ else if (grub_strncmp (sha256_oid, algo_oid, algo_oid_size) == 0)
+ {
+ if (!sha256_in_da)
+ {
+ err =
+ grub_error (GRUB_ERR_BAD_SIGNATURE,
+ "Signer %d claims a SHA-256 signature which was not specified in the outer DigestAlgorithms",
+ i);
+ goto cleanup_signerInfos;
+ }
+ else
+ {
+ sha256_in_si = true;
+ msg->signerInfos[i].hash =
+ grub_crypto_lookup_md_by_name ("sha256");
+ }
+ }
+ else
+ {
+ err =
+ grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
+ "Only SHA-256 and SHA-512 hashes are supported, found OID %s",
+ algo_oid);
+ goto cleanup_signerInfos;
+ }
+
+ if (!msg->signerInfos[i].hash)
+ {
+ err =
+ grub_error (GRUB_ERR_BAD_SIGNATURE,
+ "Hash algorithm for signer %d (OID %s) not loaded", i,
+ algo_oid);
+ goto cleanup_signerInfos;
+ }
+
+ si_sig_path = grub_xasprintf ("signerInfos.?%d.signature", i + 1);
+ if (!si_sig_path)
+ {
+ err = grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "Could not allocate path for signer %d's signature parsing path",
+ i);
+ goto cleanup_signerInfos;
+ }
+
+ result_buf =
+ grub_asn1_allocate_and_read (signed_part, si_sig_path,
+ "signature data", &result_size);
+ if (!result_buf)
+ {
+ err = grub_errno;
+ grub_free (si_sig_path);
+ goto cleanup_signerInfos;
+ }
+ grub_free (si_sig_path);
+
+ gcry_err =
+ gcry_mpi_scan (&(msg->signerInfos[i].sig_mpi), GCRYMPI_FMT_USG,
+ result_buf, result_size, NULL);
+ if (gcry_err != GPG_ERR_NO_ERROR)
+ {
+ err =
+ grub_error (GRUB_ERR_BAD_SIGNATURE,
+ "Error loading signature %d into MPI structure: %d",
+ i, gcry_err);
+ grub_free (result_buf);
+ goto cleanup_signerInfos;
+ }
+
+ grub_free (result_buf);
+
+ /* use msg->signerInfo_count to track fully populated signerInfos so we
+ know how many we need to clean up */
+ msg->signerInfo_count++;
+ }
+
+ /* Final consistency check of signerInfo.*.digestAlgorithm vs
+ digestAlgorithms.*.algorithm. An algorithm must be present in both
+ digestAlgorithms and signerInfo or in neither. We have already checked
+ for an algorithm in signerInfo that is not in digestAlgorithms, here we
+ check for algorithms in digestAlgorithms but not in signerInfos. */
+ if (sha512_in_da && !sha512_in_si)
+ {
+ err = grub_error (GRUB_ERR_BAD_SIGNATURE,
+ "SHA-512 specified in DigestAlgorithms but did not appear in SignerInfos");
+ goto cleanup_signerInfos;
+ }
+
+ if (sha256_in_da && !sha256_in_si)
+ {
+ err = grub_error (GRUB_ERR_BAD_SIGNATURE,
+ "SHA-256 specified in DigestAlgorithms but did not appear in SignerInfos");
+ goto cleanup_signerInfos;
+ }
+
+ asn1_delete_structure (&signed_part);
+ return GRUB_ERR_NONE;
+
+cleanup_signerInfos:
+ for (i = 0; i < msg->signerInfo_count; i++)
+ gcry_mpi_release (msg->signerInfos[i].sig_mpi);
+ grub_free (msg->signerInfos);
+cleanup_signed_part:
+ asn1_delete_structure (&signed_part);
+ return err;
+}
+
+grub_err_t
+parse_pkcs7_signedData (const void *sigbuf, grub_size_t data_size,
+ struct pkcs7_signedData *msg)
+{
+ int res;
+ asn1_node content_info;
+ grub_err_t err = GRUB_ERR_NONE;
+ char content_oid[MAX_OID_LEN];
+ grub_uint8_t *content;
+ int content_size;
+ int content_oid_size = sizeof (content_oid);
+ int size;
+
+ if (data_size > GRUB_INT_MAX)
+ return grub_error (GRUB_ERR_OUT_OF_RANGE,
+ "Cannot parse a PKCS#7 message where data size > INT_MAX");
+ size = (int) data_size;
+
+ res = asn1_create_element (_gnutls_pkix_asn,
+ "PKIX1.pkcs-7-ContentInfo", &content_info);
+ if (res != ASN1_SUCCESS)
+ {
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "Could not create ASN.1 structure for PKCS#7 data: %s",
+ asn1_strerror (res));
+ }
+
+ res = asn1_der_decoding2 (&content_info, sigbuf, &size,
+ ASN1_DECODE_FLAG_STRICT_DER |
+ ASN1_DECODE_FLAG_ALLOW_PADDING, asn1_error);
+ if (res != ASN1_SUCCESS)
+ {
+ err =
+ grub_error (GRUB_ERR_BAD_SIGNATURE,
+ "Error decoding PKCS#7 message DER: %s", asn1_error);
+ goto cleanup;
+ }
+
+ /*
+ * ContentInfo ::= SEQUENCE {
+ * contentType ContentType,
+ * content [0] EXPLICIT ANY DEFINED BY contentType }
+ *
+ * ContentType ::= OBJECT IDENTIFIER
+ */
+ res =
+ asn1_read_value (content_info, "contentType", content_oid,
+ &content_oid_size);
+ if (res != ASN1_SUCCESS)
+ {
+ err =
+ grub_error (GRUB_ERR_BAD_SIGNATURE,
+ "Error reading PKCS#7 content type: %s",
+ asn1_strerror (res));
+ goto cleanup;
+ }
+
+ /* OID for SignedData defined in 5.1 */
+ if (grub_strncmp (signedData_oid, content_oid, content_oid_size) != 0)
+ {
+ err =
+ grub_error (GRUB_ERR_BAD_SIGNATURE,
+ "Unexpected content type in PKCS#7 message: OID %s",
+ content_oid);
+ goto cleanup;
+ }
+
+ content =
+ grub_asn1_allocate_and_read (content_info, "content",
+ "PKCS#7 message content", &content_size);
+ if (!content)
+ {
+ err = grub_errno;
+ goto cleanup;
+ }
+
+ err = process_content (content, content_size, msg);
+ grub_free (content);
+
+cleanup:
+ asn1_delete_structure (&content_info);
+ return err;
+}
+
+/*
+ * Release all the storage associated with the PKCS#7 message.
+ * If the caller dynamically allocated the message, it must free it.
+ */
+void
+pkcs7_signedData_release (struct pkcs7_signedData *msg)
+{
+ grub_ssize_t i;
+ for (i = 0; i < msg->signerInfo_count; i++)
+ {
+ gcry_mpi_release (msg->signerInfos[i].sig_mpi);
+ }
+ grub_free (msg->signerInfos);
+}
diff --git a/grub-core/commands/appendedsig/x509.c b/grub-core/commands/appendedsig/x509.c
new file mode 100644
index 000000000..70480aa73
--- /dev/null
+++ b/grub-core/commands/appendedsig/x509.c
@@ -0,0 +1,1079 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2020 IBM Corporation.
+ *
+ * GRUB 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, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB 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 GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/libtasn1.h>
+#include <grub/types.h>
+#include <grub/err.h>
+#include <grub/mm.h>
+#include <grub/crypto.h>
+#include <grub/misc.h>
+#include <grub/gcrypt/gcrypt.h>
+
+#include "appendedsig.h"
+
+static char asn1_error[ASN1_MAX_ERROR_DESCRIPTION_SIZE];
+
+/*
+ * RFC 3279 2.3.1 RSA Keys
+ */
+const char *rsaEncryption_oid = "1.2.840.113549.1.1.1";
+
+/*
+ * RFC 5280 Appendix A
+ */
+const char *commonName_oid = "2.5.4.3";
+
+/*
+ * RFC 5280 4.2.1.3 Key Usage
+ */
+const char *keyUsage_oid = "2.5.29.15";
+
+const grub_uint8_t digitalSignatureUsage = 0x80;
+
+/*
+ * RFC 5280 4.2.1.9 Basic Constraints
+ */
+const char *basicConstraints_oid = "2.5.29.19";
+
+/*
+ * RFC 5280 4.2.1.12 Extended Key Usage
+ */
+const char *extendedKeyUsage_oid = "2.5.29.37";
+const char *codeSigningUsage_oid = "1.3.6.1.5.5.7.3.3";
+
+/*
+ * RFC 3279 2.3.1
+ *
+ * The RSA public key MUST be encoded using the ASN.1 type RSAPublicKey:
+ *
+ * RSAPublicKey ::= SEQUENCE {
+ * modulus INTEGER, -- n
+ * publicExponent INTEGER } -- e
+ *
+ * where modulus is the modulus n, and publicExponent is the public
+ * exponent e.
+ */
+static grub_err_t
+grub_parse_rsa_pubkey (grub_uint8_t *der, int dersize,
+ struct x509_certificate *certificate)
+{
+ int result;
+ asn1_node spk = NULL;
+ grub_uint8_t *m_data, *e_data;
+ int m_size, e_size;
+ grub_err_t err = GRUB_ERR_NONE;
+ gcry_error_t gcry_err;
+
+ result =
+ asn1_create_element (_gnutls_gnutls_asn, "GNUTLS.RSAPublicKey", &spk);
+ if (result != ASN1_SUCCESS)
+ {
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "Cannot create storage for public key ASN.1 data");
+ }
+
+ result = asn1_der_decoding2 (&spk, der, &dersize,
+ ASN1_DECODE_FLAG_STRICT_DER, asn1_error);
+ if (result != ASN1_SUCCESS)
+ {
+ err =
+ grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ "Cannot decode certificate public key DER: %s",
+ asn1_error);
+ goto cleanup;
+ }
+
+ m_data =
+ grub_asn1_allocate_and_read (spk, "modulus", "RSA modulus", &m_size);
+ if (!m_data)
+ {
+ err = grub_errno;
+ goto cleanup;
+ }
+
+ e_data =
+ grub_asn1_allocate_and_read (spk, "publicExponent", "RSA public exponent",
+ &e_size);
+ if (!e_data)
+ {
+ err = grub_errno;
+ goto cleanup_m_data;
+ }
+
+ /*
+ * convert m, e to mpi
+ *
+ * nscanned is not set for FMT_USG, it's only set for FMT_PGP,
+ * so we can't verify it
+ */
+ gcry_err =
+ gcry_mpi_scan (&certificate->mpis[0], GCRYMPI_FMT_USG, m_data, m_size,
+ NULL);
+ if (gcry_err != GPG_ERR_NO_ERROR)
+ {
+ err =
+ grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ "Error loading RSA modulus into MPI structure: %d",
+ gcry_err);
+ goto cleanup_e_data;
+ }
+
+ gcry_err =
+ gcry_mpi_scan (&certificate->mpis[1], GCRYMPI_FMT_USG, e_data, e_size,
+ NULL);
+ if (gcry_err != GPG_ERR_NO_ERROR)
+ {
+ err =
+ grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ "Error loading RSA exponent into MPI structure: %d",
+ gcry_err);
+ goto cleanup_m_mpi;
+ }
+
+ grub_free (e_data);
+ grub_free (m_data);
+ asn1_delete_structure (&spk);
+ return GRUB_ERR_NONE;
+
+cleanup_m_mpi:
+ gcry_mpi_release (certificate->mpis[0]);
+cleanup_e_data:
+ grub_free (e_data);
+cleanup_m_data:
+ grub_free (m_data);
+cleanup:
+ asn1_delete_structure (&spk);
+ return err;
+}
+
+
+/*
+ * RFC 5280:
+ * SubjectPublicKeyInfo ::= SEQUENCE {
+ * algorithm AlgorithmIdentifier,
+ * subjectPublicKey BIT STRING }
+ *
+ * AlgorithmIdentifiers come from RFC 3279, we are not strictly compilant as we
+ * only support RSA Encryption.
+ */
+
+static grub_err_t
+grub_x509_read_subject_public_key (asn1_node asn,
+ struct x509_certificate *results)
+{
+ int result;
+ grub_err_t err;
+ const char *algo_name =
+ "tbsCertificate.subjectPublicKeyInfo.algorithm.algorithm";
+ const char *params_name =
+ "tbsCertificate.subjectPublicKeyInfo.algorithm.parameters";
+ const char *pk_name =
+ "tbsCertificate.subjectPublicKeyInfo.subjectPublicKey";
+ char algo_oid[MAX_OID_LEN];
+ int algo_size = sizeof (algo_oid);
+ char params_value[2];
+ int params_size = sizeof (params_value);
+ grub_uint8_t *key_data = NULL;
+ int key_size = 0;
+ unsigned int key_type;
+
+ /* algorithm: see notes for rsaEncryption_oid */
+ result = asn1_read_value (asn, algo_name, algo_oid, &algo_size);
+ if (result != ASN1_SUCCESS)
+ {
+ return grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ "Error reading x509 public key algorithm: %s",
+ asn1_strerror (result));
+ }
+
+ if (grub_strncmp (algo_oid, rsaEncryption_oid, sizeof (rsaEncryption_oid))
+ != 0)
+ {
+ return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
+ "Unsupported x509 public key algorithm: %s",
+ algo_oid);
+ }
+
+ /*
+ * RFC 3279 2.3.1
+ * The rsaEncryption OID is intended to be used in the algorithm field
+ * of a value of type AlgorithmIdentifier. The parameters field MUST
+ * have ASN.1 type NULL for this algorithm identifier.
+ */
+ result = asn1_read_value (asn, params_name, params_value, &params_size);
+ if (result != ASN1_SUCCESS)
+ {
+ return grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ "Error reading x509 public key parameters: %s",
+ asn1_strerror (result));
+ }
+
+ if (params_value[0] != ASN1_TAG_NULL)
+ {
+ return grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ "Invalid x509 public key parameters: expected NULL");
+ }
+
+ /*
+ * RFC 3279 2.3.1: The DER encoded RSAPublicKey is the value of the BIT
+ * STRING subjectPublicKey.
+ */
+ result = asn1_read_value_type (asn, pk_name, NULL, &key_size, &key_type);
+ if (result != ASN1_MEM_ERROR)
+ {
+ return grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ "Error reading size of x509 public key: %s",
+ asn1_strerror (result));
+ }
+ if (key_type != ASN1_ETYPE_BIT_STRING)
+ {
+ return grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ "Unexpected ASN.1 type when reading x509 public key: %x",
+ key_type);
+ }
+
+ /* length is in bits */
+ key_size = (key_size + 7) / 8;
+
+ key_data = grub_malloc (key_size);
+ if (!key_data)
+ {
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "Out of memory for x509 public key");
+ }
+
+ result = asn1_read_value (asn, pk_name, key_data, &key_size);
+ if (result != ASN1_SUCCESS)
+ {
+ grub_free (key_data);
+ return grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ "Error reading public key data");
+ }
+ key_size = (key_size + 7) / 8;
+
+ err = grub_parse_rsa_pubkey (key_data, key_size, results);
+ grub_free (key_data);
+
+ return err;
+}
+
+/* Decode a string as defined in Appendix A */
+static grub_err_t
+decode_string (char *der, int der_size, char **string,
+ grub_size_t *string_size)
+{
+ asn1_node strasn;
+ int result;
+ char *choice;
+ int choice_size = 0;
+ int tmp_size = 0;
+ grub_err_t err = GRUB_ERR_NONE;
+
+ result =
+ asn1_create_element (_gnutls_pkix_asn, "PKIX1.DirectoryString", &strasn);
+ if (result != ASN1_SUCCESS)
+ {
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "Could not create ASN.1 structure for certificate: %s",
+ asn1_strerror (result));
+ }
+
+ result = asn1_der_decoding2 (&strasn, der, &der_size,
+ ASN1_DECODE_FLAG_STRICT_DER, asn1_error);
+ if (result != ASN1_SUCCESS)
+ {
+ err =
+ grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ "Could not parse DER for DirectoryString: %s",
+ asn1_error);
+ goto cleanup;
+ }
+
+ choice =
+ grub_asn1_allocate_and_read (strasn, "", "DirectoryString choice",
+ &choice_size);
+ if (!choice)
+ {
+ err = grub_errno;
+ goto cleanup;
+ }
+
+ if (grub_strncmp ("utf8String", choice, choice_size) == 0)
+ {
+ result = asn1_read_value (strasn, "utf8String", NULL, &tmp_size);
+ if (result != ASN1_MEM_ERROR)
+ {
+ err =
+ grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ "Error reading size of UTF-8 string: %s",
+ asn1_strerror (result));
+ goto cleanup_choice;
+ }
+ }
+ else if (grub_strncmp ("printableString", choice, choice_size) == 0)
+ {
+ result = asn1_read_value (strasn, "printableString", NULL, &tmp_size);
+ if (result != ASN1_MEM_ERROR)
+ {
+ err =
+ grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ "Error reading size of UTF-8 string: %s",
+ asn1_strerror (result));
+ goto cleanup_choice;
+ }
+ }
+ else
+ {
+ err =
+ grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
+ "Only UTF-8 and printable DirectoryStrings are supported, got %s",
+ choice);
+ goto cleanup_choice;
+ }
+
+ /* read size does not include trailing null */
+ tmp_size++;
+
+ *string = grub_malloc (tmp_size);
+ if (!*string)
+ {
+ err =
+ grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "Cannot allocate memory for DirectoryString contents");
+ goto cleanup_choice;
+ }
+
+ result = asn1_read_value (strasn, choice, *string, &tmp_size);
+ if (result != ASN1_SUCCESS)
+ {
+ err =
+ grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ "Error reading out %s in DirectoryString: %s",
+ choice, asn1_strerror (result));
+ grub_free (*string);
+ goto cleanup_choice;
+ }
+ *string_size = tmp_size + 1;
+ (*string)[tmp_size] = '\0';
+
+cleanup_choice:
+ grub_free (choice);
+cleanup:
+ asn1_delete_structure (&strasn);
+ return err;
+}
+
+/*
+ * TBSCertificate ::= SEQUENCE {
+ * version [0] EXPLICIT Version DEFAULT v1,
+ * ...
+ *
+ * Version ::= INTEGER { v1(0), v2(1), v3(2) }
+ */
+static grub_err_t
+check_version (asn1_node certificate)
+{
+ int rc;
+ const char *name = "tbsCertificate.version";
+ grub_uint8_t version;
+ int len = sizeof (version);
+
+ rc = asn1_read_value (certificate, name, &version, &len);
+
+ /* require version 3 */
+ if (rc != ASN1_SUCCESS || len != 1)
+ return grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ "Error reading certificate version");
+
+ if (version != 0x02)
+ return grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ "Invalid x509 certificate version, expected v3 (0x02), got 0x%02x",
+ version);
+
+ return GRUB_ERR_NONE;
+}
+
+/*
+ * This is an X.501 Name, which is complex.
+ *
+ * For simplicity, we extract only the CN.
+ */
+static grub_err_t
+read_name (asn1_node asn, const char *name_path, char **name,
+ grub_size_t *name_size)
+{
+ int seq_components, set_components;
+ int result;
+ int i, j;
+ char *top_path, *set_path, *type_path, *val_path;
+ char type[MAX_OID_LEN];
+ int type_len = sizeof (type);
+ int string_size = 0;
+ char *string_der;
+ grub_err_t err;
+
+ *name = NULL;
+
+ top_path = grub_xasprintf ("%s.rdnSequence", name_path);
+ if (!top_path)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "Could not allocate memory for %s name parsing path",
+ name_path);
+
+ result = asn1_number_of_elements (asn, top_path, &seq_components);
+ if (result != ASN1_SUCCESS)
+ {
+ err =
+ grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ "Error counting name components: %s",
+ asn1_strerror (result));
+ goto cleanup;
+ }
+
+ for (i = 1; i <= seq_components; i++)
+ {
+ set_path = grub_xasprintf ("%s.?%d", top_path, i);
+ if (!set_path)
+ {
+ err =
+ grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "Could not allocate memory for %s name set parsing path",
+ name_path);
+ goto cleanup_set;
+ }
+ /* this brings us, hopefully, to a set */
+ result = asn1_number_of_elements (asn, set_path, &set_components);
+ if (result != ASN1_SUCCESS)
+ {
+ err =
+ grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ "Error counting name sub-components components (element %d): %s",
+ i, asn1_strerror (result));
+ goto cleanup_set;
+ }
+ for (j = 1; j <= set_components; j++)
+ {
+ type_path = grub_xasprintf ("%s.?%d.?%d.type", top_path, i, j);
+ if (!type_path)
+ {
+ err =
+ grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "Could not allocate memory for %s name component type path",
+ name_path);
+ goto cleanup_set;
+ }
+ type_len = sizeof (type);
+ result = asn1_read_value (asn, type_path, type, &type_len);
+ if (result != ASN1_SUCCESS)
+ {
+ err =
+ grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ "Error reading %s name component type: %s",
+ name_path, asn1_strerror (result));
+ goto cleanup_type;
+ }
+
+ if (grub_strncmp (type, commonName_oid, type_len) != 0)
+ {
+ grub_free (type_path);
+ continue;
+ }
+
+ val_path = grub_xasprintf ("%s.?%d.?%d.value", top_path, i, j);
+ if (!val_path)
+ {
+ err =
+ grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "Could not allocate memory for %s name component value path",
+ name_path);
+ goto cleanup_set;
+ }
+
+ string_der =
+ grub_asn1_allocate_and_read (asn, val_path, name_path,
+ &string_size);
+ if (!string_der)
+ {
+ err = grub_errno;
+ goto cleanup_val_path;
+ }
+
+ err = decode_string (string_der, string_size, name, name_size);
+ if (err)
+ goto cleanup_string;
+
+ grub_free (string_der);
+ grub_free (type_path);
+ grub_free (val_path);
+ break;
+ }
+ grub_free (set_path);
+
+ if (*name)
+ break;
+ }
+
+ grub_free (top_path);
+
+ return GRUB_ERR_NONE;
+
+cleanup_string:
+ grub_free (string_der);
+cleanup_val_path:
+ grub_free (val_path);
+cleanup_type:
+ grub_free (type_path);
+cleanup_set:
+ grub_free (set_path);
+cleanup:
+ grub_free (top_path);
+ return err;
+}
+
+/*
+ * Verify the Key Usage extension.
+ * We only permit the Digital signature usage.
+ */
+static grub_err_t
+verify_key_usage (grub_uint8_t *value, int value_size)
+{
+ asn1_node usageasn;
+ int result;
+ grub_err_t err = GRUB_ERR_NONE;
+ grub_uint8_t usage = 0xff;
+ int usage_size = sizeof (usage_size);
+
+ result =
+ asn1_create_element (_gnutls_pkix_asn, "PKIX1.KeyUsage", &usageasn);
+ if (result != ASN1_SUCCESS)
+ {
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "Could not create ASN.1 structure for key usage");
+ }
+
+ result = asn1_der_decoding2 (&usageasn, value, &value_size,
+ ASN1_DECODE_FLAG_STRICT_DER, asn1_error);
+ if (result != ASN1_SUCCESS)
+ {
+ err =
+ grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ "Error parsing DER for Key Usage: %s", asn1_error);
+ goto cleanup;
+ }
+
+ result = asn1_read_value (usageasn, "", &usage, &usage_size);
+ if (result != ASN1_SUCCESS)
+ {
+ err =
+ grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ "Error reading Key Usage value: %s",
+ asn1_strerror (result));
+ goto cleanup;
+ }
+
+ if (usage != digitalSignatureUsage)
+ {
+ err =
+ grub_error (GRUB_ERR_BAD_FILE_TYPE, "Unexpected Key Usage value: %x",
+ usage);
+ goto cleanup;
+ }
+
+cleanup:
+ asn1_delete_structure (&usageasn);
+ return err;
+}
+
+/*
+ * BasicConstraints ::= SEQUENCE {
+ * cA BOOLEAN DEFAULT FALSE,
+ * pathLenConstraint INTEGER (0..MAX) OPTIONAL }
+ */
+static grub_err_t
+verify_basic_constraints (grub_uint8_t *value, int value_size)
+{
+ asn1_node basicasn;
+ int result;
+ grub_err_t err = GRUB_ERR_NONE;
+ char cA[6]; /* FALSE or TRUE */
+ int cA_size = sizeof (cA);
+
+ result =
+ asn1_create_element (_gnutls_pkix_asn, "PKIX1.BasicConstraints",
+ &basicasn);
+ if (result != ASN1_SUCCESS)
+ {
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "Could not create ASN.1 structure for Basic Constraints");
+ }
+
+ result = asn1_der_decoding2 (&basicasn, value, &value_size,
+ ASN1_DECODE_FLAG_STRICT_DER, asn1_error);
+ if (result != ASN1_SUCCESS)
+ {
+ err =
+ grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ "Error parsing DER for Basic Constraints: %s",
+ asn1_error);
+ goto cleanup;
+ }
+
+ result = asn1_read_value (basicasn, "cA", cA, &cA_size);
+ if (result == ASN1_ELEMENT_NOT_FOUND)
+ {
+ /* Not present, default is False, so this is OK */
+ err = GRUB_ERR_NONE;
+ goto cleanup;
+ }
+ else if (result != ASN1_SUCCESS)
+ {
+ err =
+ grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ "Error reading Basic Constraints cA value: %s",
+ asn1_strerror (result));
+ goto cleanup;
+ }
+
+ /* The certificate must not be a CA certificate */
+ if (grub_strncmp ("FALSE", cA, cA_size) != 0)
+ {
+ err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "Unexpected CA value: %s",
+ cA);
+ goto cleanup;
+ }
+
+cleanup:
+ asn1_delete_structure (&basicasn);
+ return err;
+}
+
+/*
+ * ExtKeyUsageSyntax ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId
+ *
+ * KeyPurposeId ::= OBJECT IDENTIFIER
+ */
+static grub_err_t
+verify_extended_key_usage (grub_uint8_t *value, int value_size)
+{
+ asn1_node extendedasn;
+ int result, count;
+ grub_err_t err = GRUB_ERR_NONE;
+ char usage[MAX_OID_LEN];
+ int usage_size = sizeof (usage);
+
+ result =
+ asn1_create_element (_gnutls_pkix_asn, "PKIX1.ExtKeyUsageSyntax",
+ &extendedasn);
+ if (result != ASN1_SUCCESS)
+ {
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "Could not create ASN.1 structure for Extended Key Usage");
+ }
+
+ result = asn1_der_decoding2 (&extendedasn, value, &value_size,
+ ASN1_DECODE_FLAG_STRICT_DER, asn1_error);
+ if (result != ASN1_SUCCESS)
+ {
+ err =
+ grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ "Error parsing DER for Extended Key Usage: %s",
+ asn1_error);
+ goto cleanup;
+ }
+
+ /*
+ * If EKUs are present, there must be exactly 1 and it must be a
+ * codeSigning usage.
+ */
+ result = asn1_number_of_elements (extendedasn, "", &count);
+ if (result != ASN1_SUCCESS)
+ {
+ err =
+ grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ "Error counting number of Extended Key Usages: %s",
+ asn1_strerror (result));
+ goto cleanup;
+ }
+
+ if (count != 1)
+ {
+ err =
+ grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ "Unexpected number of Extended Key Usages: %d, 1 expected",
+ count);
+ goto cleanup;
+ }
+
+ result = asn1_read_value (extendedasn, "?1", usage, &usage_size);
+ if (result != ASN1_SUCCESS)
+ {
+ err =
+ grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ "Error reading Extended Key Usage: %s",
+ asn1_strerror (result));
+ goto cleanup;
+ }
+
+ if (grub_strncmp (codeSigningUsage_oid, usage, usage_size) != 0)
+ {
+ err =
+ grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ "Unexpected Extended Key Usage OID, got: %s", usage);
+ goto cleanup;
+ }
+
+cleanup:
+ asn1_delete_structure (&extendedasn);
+ return err;
+}
+
+/*
+ * Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension
+ *
+ * Extension ::= SEQUENCE {
+ * extnID OBJECT IDENTIFIER,
+ * critical BOOLEAN DEFAULT FALSE,
+ * extnValue OCTET STRING
+ * -- contains the DER encoding of an ASN.1 value
+ * -- corresponding to the extension type identified
+ * -- by extnID
+ * }
+ *
+ * A certificate must:
+ * - contain the Digital Signature usage only
+ * - not be a CA
+ * - contain no extended usages, or only a code signing extended usage
+ * - not contain any other critical extensions (RFC 5280 s 4.2)
+ */
+static grub_err_t
+verify_extensions (asn1_node cert)
+{
+ int result;
+ int ext, num_extensions = 0;
+ int usage_present = 0, constraints_present = 0, extended_usage_present = 0;
+ char *oid_path, *critical_path, *value_path;
+ char extnID[MAX_OID_LEN];
+ int extnID_size;
+ grub_err_t err;
+ char critical[6]; /* we get either "TRUE" or "FALSE" */
+ int critical_size;
+ grub_uint8_t *value;
+ int value_size;
+
+ result =
+ asn1_number_of_elements (cert, "tbsCertificate.extensions",
+ &num_extensions);
+ if (result != ASN1_SUCCESS)
+ {
+ return grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ "Error counting number of extensions: %s",
+ asn1_strerror (result));
+ }
+
+ if (num_extensions < 2)
+ {
+ return grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ "Insufficient number of extensions for certificate, need at least 2, got %d",
+ num_extensions);
+ }
+
+ for (ext = 1; ext <= num_extensions; ext++)
+ {
+ oid_path = grub_xasprintf ("tbsCertificate.extensions.?%d.extnID", ext);
+
+ extnID_size = sizeof (extnID);
+ result = asn1_read_value (cert, oid_path, extnID, &extnID_size);
+ if (result != ASN1_SUCCESS)
+ {
+ err =
+ grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ "Error reading extension OID: %s",
+ asn1_strerror (result));
+ goto cleanup_oid_path;
+ }
+
+ critical_path =
+ grub_xasprintf ("tbsCertificate.extensions.?%d.critical", ext);
+ critical_size = sizeof (critical);
+ result =
+ asn1_read_value (cert, critical_path, critical, &critical_size);
+ if (result == ASN1_ELEMENT_NOT_FOUND)
+ {
+ critical[0] = '\0';
+ }
+ else if (result != ASN1_SUCCESS)
+ {
+ err =
+ grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ "Error reading extension criticality: %s",
+ asn1_strerror (result));
+ goto cleanup_critical_path;
+ }
+
+ value_path =
+ grub_xasprintf ("tbsCertificate.extensions.?%d.extnValue", ext);
+ value =
+ grub_asn1_allocate_and_read (cert, value_path,
+ "certificate extension value",
+ &value_size);
+ if (!value)
+ {
+ err = grub_errno;
+ goto cleanup_value_path;
+ }
+
+ /*
+ * Now we must see if we recognise the OID.
+ * If we have an unrecognised critical extension we MUST bail.
+ */
+ if (grub_strncmp (keyUsage_oid, extnID, extnID_size) == 0)
+ {
+ err = verify_key_usage (value, value_size);
+ if (err != GRUB_ERR_NONE)
+ {
+ goto cleanup_value;
+ }
+ usage_present++;
+ }
+ else if (grub_strncmp (basicConstraints_oid, extnID, extnID_size) == 0)
+ {
+ err = verify_basic_constraints (value, value_size);
+ if (err != GRUB_ERR_NONE)
+ {
+ goto cleanup_value;
+ }
+ constraints_present++;
+ }
+ else if (grub_strncmp (extendedKeyUsage_oid, extnID, extnID_size) == 0)
+ {
+ err = verify_extended_key_usage (value, value_size);
+ if (err != GRUB_ERR_NONE)
+ {
+ goto cleanup_value;
+ }
+ extended_usage_present++;
+ }
+ else if (grub_strncmp ("TRUE", critical, critical_size) == 0)
+ {
+ /*
+ * per the RFC, we must not process a certificate with
+ * a critical extension we do not understand.
+ */
+ err =
+ grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ "Unhandled critical x509 extension with OID %s",
+ extnID);
+ goto cleanup_value;
+ }
+
+ grub_free (value);
+ grub_free (value_path);
+ grub_free (critical_path);
+ grub_free (oid_path);
+ }
+
+ if (usage_present != 1)
+ {
+ return grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ "Unexpected number of Key Usage extensions - expected 1, got %d",
+ usage_present);
+ }
+ if (constraints_present != 1)
+ {
+ return grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ "Unexpected number of basic constraints extensions - expected 1, got %d",
+ constraints_present);
+ }
+ if (extended_usage_present > 1)
+ {
+ return grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ "Unexpected number of Extended Key Usage extensions - expected 0 or 1, got %d",
+ extended_usage_present);
+ }
+ return GRUB_ERR_NONE;
+
+cleanup_value:
+ grub_free (value);
+cleanup_value_path:
+ grub_free (value_path);
+cleanup_critical_path:
+ grub_free (critical_path);
+cleanup_oid_path:
+ grub_free (oid_path);
+ return err;
+}
+
+/*
+ * Parse a certificate whose DER-encoded form is in @data, of size @data_size.
+ * Return the results in @results, which must point to an allocated x509 certificate.
+ */
+grub_err_t
+parse_x509_certificate (const void *data, grub_size_t data_size,
+ struct x509_certificate *results)
+{
+ int result = 0;
+ asn1_node cert;
+ grub_err_t err;
+ int size;
+ int tmp_size;
+
+ if (data_size > GRUB_INT_MAX)
+ return grub_error (GRUB_ERR_OUT_OF_RANGE,
+ "Cannot parse a certificate where data size > INT_MAX");
+ size = (int) data_size;
+
+ result = asn1_create_element (_gnutls_pkix_asn, "PKIX1.Certificate", &cert);
+ if (result != ASN1_SUCCESS)
+ {
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "Could not create ASN.1 structure for certificate: %s",
+ asn1_strerror (result));
+ }
+
+ result = asn1_der_decoding2 (&cert, data, &size,
+ ASN1_DECODE_FLAG_STRICT_DER, asn1_error);
+ if (result != ASN1_SUCCESS)
+ {
+ err =
+ grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ "Could not parse DER for certificate: %s", asn1_error);
+ goto cleanup;
+ }
+
+ /*
+ * TBSCertificate ::= SEQUENCE {
+ * version [0] EXPLICIT Version DEFAULT v1
+ */
+ err = check_version (cert);
+ if (err != GRUB_ERR_NONE)
+ {
+ goto cleanup;
+ }
+
+ /*
+ * serialNumber CertificateSerialNumber,
+ *
+ * CertificateSerialNumber ::= INTEGER
+ */
+ results->serial =
+ grub_asn1_allocate_and_read (cert, "tbsCertificate.serialNumber",
+ "certificate serial number", &tmp_size);
+ if (!results->serial)
+ {
+ err = grub_errno;
+ goto cleanup;
+ }
+ /*
+ * It's safe to cast the signed int to an unsigned here, we know
+ * length is non-negative
+ */
+ results->serial_len = tmp_size;
+
+ /*
+ * signature AlgorithmIdentifier,
+ *
+ * We don't load the signature or issuer at the moment,
+ * as we don't attempt x509 verification.
+ */
+
+ /*
+ * issuer Name,
+ *
+ * The RFC only requires the serial number to be unique within
+ * issuers, so to avoid ambiguity we _technically_ ought to make
+ * this available.
+ */
+
+ /*
+ * validity Validity,
+ *
+ * Validity ::= SEQUENCE {
+ * notBefore Time,
+ * notAfter Time }
+ *
+ * We can't validate this reasonably, we have no true time source on several
+ * platforms. For now we do not parse them.
+ */
+
+ /*
+ * subject Name,
+ *
+ * This is an X501 name, we parse out just the CN.
+ */
+ err =
+ read_name (cert, "tbsCertificate.subject", &results->subject,
+ &results->subject_len);
+ if (err != GRUB_ERR_NONE)
+ goto cleanup_serial;
+
+ /*
+ * TBSCertificate ::= SEQUENCE {
+ * ...
+ * subjectPublicKeyInfo SubjectPublicKeyInfo,
+ * ...
+ */
+ err = grub_x509_read_subject_public_key (cert, results);
+ if (err != GRUB_ERR_NONE)
+ goto cleanup_name;
+
+ /*
+ * TBSCertificate ::= SEQUENCE {
+ * ...
+ * extensions [3] EXPLICIT Extensions OPTIONAL
+ * -- If present, version MUST be v3
+ * }
+ */
+
+ err = verify_extensions (cert);
+ if (err != GRUB_ERR_NONE)
+ goto cleanup_mpis;
+
+ /*
+ * We do not read or check the signature on the certificate:
+ * as discussed we do not try to validate the certificate but trust
+ * it implictly.
+ */
+
+ asn1_delete_structure (&cert);
+ return GRUB_ERR_NONE;
+
+cleanup_mpis:
+ gcry_mpi_release (results->mpis[0]);
+ gcry_mpi_release (results->mpis[1]);
+cleanup_name:
+ grub_free (results->subject);
+cleanup_serial:
+ grub_free (results->serial);
+cleanup:
+ asn1_delete_structure (&cert);
+ return err;
+}
+
+/*
+ * Release all the storage associated with the x509 certificate.
+ * If the caller dynamically allocated the certificate, it must free it.
+ * The caller is also responsible for maintenance of the linked list.
+ */
+void
+certificate_release (struct x509_certificate *cert)
+{
+ grub_free (cert->subject);
+ grub_free (cert->serial);
+ gcry_mpi_release (cert->mpis[0]);
+ gcry_mpi_release (cert->mpis[1]);
+}
--
2.41.0
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/sevever/grub2_oc.git
[email protected]:sevever/grub2_oc.git
sevever
grub2_oc
grub2_oc
master

搜索帮助