]> git.proxmox.com Git - mirror_edk2.git/blob - CryptoPkg/Library/BaseCryptLib/Pk/CryptPkcs7Verify.c
Fix several issues in BaseCryptLib:
[mirror_edk2.git] / CryptoPkg / Library / BaseCryptLib / Pk / CryptPkcs7Verify.c
1 /** @file
2 PKCS#7 SignedData Verification Wrapper Implementation over OpenSSL.
3
4 Caution: This module requires additional review when modified.
5 This library will have external input - signature (e.g. UEFI Authenticated
6 Variable). It may by input in SMM mode.
7 This external input must be validated carefully to avoid security issue like
8 buffer overflow, integer overflow.
9
10 WrapPkcs7Data(), Pkcs7GetSigners(), Pkcs7Verify() will get UEFI Authenticated
11 Variable and will do basic check for data structure.
12
13 Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.<BR>
14 This program and the accompanying materials
15 are licensed and made available under the terms and conditions of the BSD License
16 which accompanies this distribution. The full text of the license may be found at
17 http://opensource.org/licenses/bsd-license.php
18
19 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
20 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
21
22 **/
23
24 #include "InternalCryptLib.h"
25
26 #include <openssl/objects.h>
27 #include <openssl/x509.h>
28 #include <openssl/pkcs7.h>
29
30 UINT8 mOidValue[9] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x02 };
31
32 /**
33 Verification callback function to override any existing callbacks in OpenSSL
34 for intermediate certificate supports.
35
36 @param[in] Status Original status before calling this callback.
37 @param[in] Context X509 store context.
38
39 @retval 1 Current X509 certificate is verified successfully.
40 @retval 0 Verification failed.
41
42 **/
43 int
44 X509VerifyCb (
45 IN int Status,
46 IN X509_STORE_CTX *Context
47 )
48 {
49 X509_OBJECT *Obj;
50 INTN Error;
51 INTN Index;
52 INTN Count;
53
54 Obj = NULL;
55 Error = (INTN) X509_STORE_CTX_get_error (Context);
56
57 //
58 // X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT and X509_V_ERR_UNABLE_TO_GET_ISSUER_
59 // CERT_LOCALLY mean a X509 certificate is not self signed and its issuer
60 // can not be found in X509_verify_cert of X509_vfy.c.
61 // In order to support intermediate certificate node, we override the
62 // errors if the certification is obtained from X509 store, i.e. it is
63 // a trusted ceritifcate node that is enrolled by user.
64 // Besides,X509_V_ERR_CERT_UNTRUSTED and X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE
65 // are also ignored to enable such feature.
66 //
67 if ((Error == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT) ||
68 (Error == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY)) {
69 Obj = (X509_OBJECT *) malloc (sizeof (X509_OBJECT));
70 if (Obj == NULL) {
71 return 0;
72 }
73
74 Obj->type = X509_LU_X509;
75 Obj->data.x509 = Context->current_cert;
76
77 CRYPTO_w_lock (CRYPTO_LOCK_X509_STORE);
78
79 if (X509_OBJECT_retrieve_match (Context->ctx->objs, Obj)) {
80 Status = 1;
81 } else {
82 //
83 // If any certificate in the chain is enrolled as trusted certificate,
84 // pass the certificate verification.
85 //
86 if (Error == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY) {
87 Count = (INTN) sk_X509_num (Context->chain);
88 for (Index = 0; Index < Count; Index++) {
89 Obj->data.x509 = sk_X509_value (Context->chain, (int) Index);
90 if (X509_OBJECT_retrieve_match (Context->ctx->objs, Obj)) {
91 Status = 1;
92 break;
93 }
94 }
95 }
96 }
97
98 CRYPTO_w_unlock (CRYPTO_LOCK_X509_STORE);
99 }
100
101 if ((Error == X509_V_ERR_CERT_UNTRUSTED) ||
102 (Error == X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE)) {
103 Status = 1;
104 }
105
106 if (Obj != NULL) {
107 OPENSSL_free (Obj);
108 }
109
110 return Status;
111 }
112
113 /**
114 Check input P7Data is a wrapped ContentInfo structure or not. If not construct
115 a new structure to wrap P7Data.
116
117 Caution: This function may receive untrusted input.
118 UEFI Authenticated Variable is external input, so this function will do basic
119 check for PKCS#7 data structure.
120
121 @param[in] P7Data Pointer to the PKCS#7 message to verify.
122 @param[in] P7Length Length of the PKCS#7 message in bytes.
123 @param[out] WrapFlag If TRUE P7Data is a ContentInfo structure, otherwise
124 return FALSE.
125 @param[out] WrapData If return status of this function is TRUE:
126 1) when WrapFlag is TRUE, pointer to P7Data.
127 2) when WrapFlag is FALSE, pointer to a new ContentInfo
128 structure. It's caller's responsibility to free this
129 buffer.
130 @param[out] WrapDataSize Length of ContentInfo structure in bytes.
131
132 @retval TRUE The operation is finished successfully.
133 @retval FALSE The operation is failed due to lack of resources.
134
135 **/
136 BOOLEAN
137 WrapPkcs7Data (
138 IN CONST UINT8 *P7Data,
139 IN UINTN P7Length,
140 OUT BOOLEAN *WrapFlag,
141 OUT UINT8 **WrapData,
142 OUT UINTN *WrapDataSize
143 )
144 {
145 BOOLEAN Wrapped;
146 UINT8 *SignedData;
147
148 //
149 // Check whether input P7Data is a wrapped ContentInfo structure or not.
150 //
151 Wrapped = FALSE;
152 if ((P7Data[4] == 0x06) && (P7Data[5] == 0x09)) {
153 if (CompareMem (P7Data + 6, mOidValue, sizeof (mOidValue)) == 0) {
154 if ((P7Data[15] == 0xA0) && (P7Data[16] == 0x82)) {
155 Wrapped = TRUE;
156 }
157 }
158 }
159
160 if (Wrapped) {
161 *WrapData = (UINT8 *) P7Data;
162 *WrapDataSize = P7Length;
163 } else {
164 //
165 // Wrap PKCS#7 signeddata to a ContentInfo structure - add a header in 19 bytes.
166 //
167 *WrapDataSize = P7Length + 19;
168 *WrapData = malloc (*WrapDataSize);
169 if (*WrapData == NULL) {
170 *WrapFlag = Wrapped;
171 return FALSE;
172 }
173
174 SignedData = *WrapData;
175
176 //
177 // Part1: 0x30, 0x82.
178 //
179 SignedData[0] = 0x30;
180 SignedData[1] = 0x82;
181
182 //
183 // Part2: Length1 = P7Length + 19 - 4, in big endian.
184 //
185 SignedData[2] = (UINT8) (((UINT16) (*WrapDataSize - 4)) >> 8);
186 SignedData[3] = (UINT8) (((UINT16) (*WrapDataSize - 4)) & 0xff);
187
188 //
189 // Part3: 0x06, 0x09.
190 //
191 SignedData[4] = 0x06;
192 SignedData[5] = 0x09;
193
194 //
195 // Part4: OID value -- 0x2A 0x86 0x48 0x86 0xF7 0x0D 0x01 0x07 0x02.
196 //
197 CopyMem (SignedData + 6, mOidValue, sizeof (mOidValue));
198
199 //
200 // Part5: 0xA0, 0x82.
201 //
202 SignedData[15] = 0xA0;
203 SignedData[16] = 0x82;
204
205 //
206 // Part6: Length2 = P7Length, in big endian.
207 //
208 SignedData[17] = (UINT8) (((UINT16) P7Length) >> 8);
209 SignedData[18] = (UINT8) (((UINT16) P7Length) & 0xff);
210
211 //
212 // Part7: P7Data.
213 //
214 CopyMem (SignedData + 19, P7Data, P7Length);
215 }
216
217 *WrapFlag = Wrapped;
218 return TRUE;
219 }
220
221 /**
222 Get the signer's certificates from PKCS#7 signed data as described in "PKCS #7:
223 Cryptographic Message Syntax Standard". The input signed data could be wrapped
224 in a ContentInfo structure.
225
226 If P7Data, CertStack, StackLength, TrustedCert or CertLength is NULL, then
227 return FALSE. If P7Length overflow, then return FAlSE.
228
229 Caution: This function may receive untrusted input.
230 UEFI Authenticated Variable is external input, so this function will do basic
231 check for PKCS#7 data structure.
232
233 @param[in] P7Data Pointer to the PKCS#7 message to verify.
234 @param[in] P7Length Length of the PKCS#7 message in bytes.
235 @param[out] CertStack Pointer to Signer's certificates retrieved from P7Data.
236 It's caller's responsiblity to free the buffer.
237 @param[out] StackLength Length of signer's certificates in bytes.
238 @param[out] TrustedCert Pointer to a trusted certificate from Signer's certificates.
239 It's caller's responsiblity to free the buffer.
240 @param[out] CertLength Length of the trusted certificate in bytes.
241
242 @retval TRUE The operation is finished successfully.
243 @retval FALSE Error occurs during the operation.
244
245 **/
246 BOOLEAN
247 EFIAPI
248 Pkcs7GetSigners (
249 IN CONST UINT8 *P7Data,
250 IN UINTN P7Length,
251 OUT UINT8 **CertStack,
252 OUT UINTN *StackLength,
253 OUT UINT8 **TrustedCert,
254 OUT UINTN *CertLength
255 )
256 {
257 PKCS7 *Pkcs7;
258 BOOLEAN Status;
259 UINT8 *SignedData;
260 UINT8 *Temp;
261 UINTN SignedDataSize;
262 BOOLEAN Wrapped;
263 STACK_OF(X509) *Stack;
264 UINT8 Index;
265 UINT8 *CertBuf;
266 UINT8 *OldBuf;
267 UINTN BufferSize;
268 UINTN OldSize;
269 UINT8 *SingleCert;
270 UINTN SingleCertSize;
271
272 if ((P7Data == NULL) || (CertStack == NULL) || (StackLength == NULL) ||
273 (TrustedCert == NULL) || (CertLength == NULL) || (P7Length > INT_MAX)) {
274 return FALSE;
275 }
276
277 Status = WrapPkcs7Data (P7Data, P7Length, &Wrapped, &SignedData, &SignedDataSize);
278 if (!Status) {
279 return Status;
280 }
281
282 Status = FALSE;
283 Pkcs7 = NULL;
284 Stack = NULL;
285 CertBuf = NULL;
286 OldBuf = NULL;
287 SingleCert = NULL;
288
289 //
290 // Retrieve PKCS#7 Data (DER encoding)
291 //
292 if (SignedDataSize > INT_MAX) {
293 goto _Exit;
294 }
295
296 Temp = SignedData;
297 Pkcs7 = d2i_PKCS7 (NULL, (const unsigned char **) &Temp, (int) SignedDataSize);
298 if (Pkcs7 == NULL) {
299 goto _Exit;
300 }
301
302 //
303 // Check if it's PKCS#7 Signed Data (for Authenticode Scenario)
304 //
305 if (!PKCS7_type_is_signed (Pkcs7)) {
306 goto _Exit;
307 }
308
309 Stack = PKCS7_get0_signers(Pkcs7, NULL, PKCS7_BINARY);
310 if (Stack == NULL) {
311 goto _Exit;
312 }
313
314 //
315 // Convert CertStack to buffer in following format:
316 // UINT8 CertNumber;
317 // UINT32 Cert1Length;
318 // UINT8 Cert1[];
319 // UINT32 Cert2Length;
320 // UINT8 Cert2[];
321 // ...
322 // UINT32 CertnLength;
323 // UINT8 Certn[];
324 //
325 BufferSize = sizeof (UINT8);
326 OldSize = BufferSize;
327
328 for (Index = 0; ; Index++) {
329 Status = X509PopCertificate (Stack, &SingleCert, &SingleCertSize);
330 if (!Status) {
331 break;
332 }
333
334 OldSize = BufferSize;
335 OldBuf = CertBuf;
336 BufferSize = OldSize + SingleCertSize + sizeof (UINT32);
337 CertBuf = malloc (BufferSize);
338
339 if (CertBuf == NULL) {
340 goto _Exit;
341 }
342
343 if (OldBuf != NULL) {
344 CopyMem (CertBuf, OldBuf, OldSize);
345 free (OldBuf);
346 OldBuf = NULL;
347 }
348
349 WriteUnaligned32 ((UINT32 *) (CertBuf + OldSize), (UINT32) SingleCertSize);
350 CopyMem (CertBuf + OldSize + sizeof (UINT32), SingleCert, SingleCertSize);
351
352 free (SingleCert);
353 SingleCert = NULL;
354 }
355
356 if (CertBuf != NULL) {
357 //
358 // Update CertNumber.
359 //
360 CertBuf[0] = Index;
361
362 *CertLength = BufferSize - OldSize - sizeof (UINT32);
363 *TrustedCert = malloc (*CertLength);
364 if (*TrustedCert == NULL) {
365 goto _Exit;
366 }
367
368 CopyMem (*TrustedCert, CertBuf + OldSize + sizeof (UINT32), *CertLength);
369 *CertStack = CertBuf;
370 *StackLength = BufferSize;
371 Status = TRUE;
372 }
373
374 _Exit:
375 //
376 // Release Resources
377 //
378 if (!Wrapped) {
379 free (SignedData);
380 }
381
382 if (Pkcs7 != NULL) {
383 PKCS7_free (Pkcs7);
384 }
385
386 if (Stack != NULL) {
387 sk_X509_pop_free(Stack, X509_free);
388 }
389
390 if (SingleCert != NULL) {
391 free (SingleCert);
392 }
393
394 if (!Status && (CertBuf != NULL)) {
395 free (CertBuf);
396 *CertStack = NULL;
397 }
398
399 if (OldBuf != NULL) {
400 free (OldBuf);
401 }
402
403 return Status;
404 }
405
406 /**
407 Wrap function to use free() to free allocated memory for certificates.
408
409 @param[in] Certs Pointer to the certificates to be freed.
410
411 **/
412 VOID
413 EFIAPI
414 Pkcs7FreeSigners (
415 IN UINT8 *Certs
416 )
417 {
418 if (Certs == NULL) {
419 return;
420 }
421
422 free (Certs);
423 }
424
425 /**
426 Verifies the validility of a PKCS#7 signed data as described in "PKCS #7:
427 Cryptographic Message Syntax Standard". The input signed data could be wrapped
428 in a ContentInfo structure.
429
430 If P7Data, TrustedCert or InData is NULL, then return FALSE.
431 If P7Length, CertLength or DataLength overflow, then return FAlSE.
432
433 Caution: This function may receive untrusted input.
434 UEFI Authenticated Variable is external input, so this function will do basic
435 check for PKCS#7 data structure.
436
437 @param[in] P7Data Pointer to the PKCS#7 message to verify.
438 @param[in] P7Length Length of the PKCS#7 message in bytes.
439 @param[in] TrustedCert Pointer to a trusted/root certificate encoded in DER, which
440 is used for certificate chain verification.
441 @param[in] CertLength Length of the trusted certificate in bytes.
442 @param[in] InData Pointer to the content to be verified.
443 @param[in] DataLength Length of InData in bytes.
444
445 @retval TRUE The specified PKCS#7 signed data is valid.
446 @retval FALSE Invalid PKCS#7 signed data.
447
448 **/
449 BOOLEAN
450 EFIAPI
451 Pkcs7Verify (
452 IN CONST UINT8 *P7Data,
453 IN UINTN P7Length,
454 IN CONST UINT8 *TrustedCert,
455 IN UINTN CertLength,
456 IN CONST UINT8 *InData,
457 IN UINTN DataLength
458 )
459 {
460 PKCS7 *Pkcs7;
461 BIO *CertBio;
462 BIO *DataBio;
463 BOOLEAN Status;
464 X509 *Cert;
465 X509_STORE *CertStore;
466 UINT8 *SignedData;
467 UINT8 *Temp;
468 UINTN SignedDataSize;
469 BOOLEAN Wrapped;
470
471 //
472 // Check input parameters.
473 //
474 if (P7Data == NULL || TrustedCert == NULL || InData == NULL ||
475 P7Length > INT_MAX || CertLength > INT_MAX || DataLength > INT_MAX) {
476 return FALSE;
477 }
478
479 Pkcs7 = NULL;
480 CertBio = NULL;
481 DataBio = NULL;
482 Cert = NULL;
483 CertStore = NULL;
484
485 //
486 // Register & Initialize necessary digest algorithms for PKCS#7 Handling
487 //
488 if (EVP_add_digest (EVP_md5 ()) == 0) {
489 return FALSE;
490 }
491 if (EVP_add_digest (EVP_sha1 ()) == 0) {
492 return FALSE;
493 }
494 if (EVP_add_digest (EVP_sha256 ()) == 0) {
495 return FALSE;
496 }
497 if (EVP_add_digest_alias (SN_sha1WithRSAEncryption, SN_sha1WithRSA) == 0) {
498 return FALSE;
499 }
500
501
502 Status = WrapPkcs7Data (P7Data, P7Length, &Wrapped, &SignedData, &SignedDataSize);
503 if (!Status) {
504 return Status;
505 }
506
507 Status = FALSE;
508
509 //
510 // Retrieve PKCS#7 Data (DER encoding)
511 //
512 if (SignedDataSize > INT_MAX) {
513 goto _Exit;
514 }
515
516 Temp = SignedData;
517 Pkcs7 = d2i_PKCS7 (NULL, (const unsigned char **) &Temp, (int) SignedDataSize);
518 if (Pkcs7 == NULL) {
519 goto _Exit;
520 }
521
522 //
523 // Check if it's PKCS#7 Signed Data (for Authenticode Scenario)
524 //
525 if (!PKCS7_type_is_signed (Pkcs7)) {
526 goto _Exit;
527 }
528
529 //
530 // Read DER-encoded root certificate and Construct X509 Certificate
531 //
532 CertBio = BIO_new (BIO_s_mem ());
533 BIO_write (CertBio, TrustedCert, (int)CertLength);
534 if (CertBio == NULL) {
535 goto _Exit;
536 }
537 Cert = d2i_X509_bio (CertBio, NULL);
538 if (Cert == NULL) {
539 goto _Exit;
540 }
541
542 //
543 // Setup X509 Store for trusted certificate
544 //
545 CertStore = X509_STORE_new ();
546 if (CertStore == NULL) {
547 goto _Exit;
548 }
549 if (!(X509_STORE_add_cert (CertStore, Cert))) {
550 goto _Exit;
551 }
552
553 //
554 // Register customized X509 verification callback function to support
555 // trusted intermediate certificate anchor.
556 //
557 CertStore->verify_cb = X509VerifyCb;
558
559 //
560 // For generic PKCS#7 handling, InData may be NULL if the content is present
561 // in PKCS#7 structure. So ignore NULL checking here.
562 //
563 DataBio = BIO_new (BIO_s_mem ());
564 BIO_write (DataBio, InData, (int)DataLength);
565
566 //
567 // Verifies the PKCS#7 signedData structure
568 //
569 Status = (BOOLEAN) PKCS7_verify (Pkcs7, NULL, CertStore, DataBio, NULL, PKCS7_BINARY);
570
571 _Exit:
572 //
573 // Release Resources
574 //
575 BIO_free (DataBio);
576 BIO_free (CertBio);
577 X509_free (Cert);
578 X509_STORE_free (CertStore);
579 PKCS7_free (Pkcs7);
580
581 if (!Wrapped) {
582 OPENSSL_free (SignedData);
583 }
584
585 return Status;
586 }