]> git.proxmox.com Git - mirror_edk2.git/blob - CryptoPkg/Library/BaseCryptLib/Pk/CryptPkcs7Verify.c
Fix PeiCryptLib build issue.
[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 Pop single certificate from STACK_OF(X509).
223
224 If X509Stack, Cert, or CertSize is NULL, then return FALSE.
225
226 @param[in] X509Stack Pointer to a X509 stack object.
227 @param[out] Cert Pointer to a X509 certificate.
228 @param[out] CertSize Length of output X509 certificate in bytes.
229
230 @retval TRUE The X509 stack pop succeeded.
231 @retval FALSE The pop operation failed.
232
233 **/
234 BOOLEAN
235 X509PopCertificate (
236 IN VOID *X509Stack,
237 OUT UINT8 **Cert,
238 OUT UINTN *CertSize
239 )
240 {
241 BIO *CertBio;
242 X509 *X509Cert;
243 STACK_OF(X509) *CertStack;
244 BOOLEAN Status;
245 int Result;
246 int Length;
247 VOID *Buffer;
248
249 Status = FALSE;
250
251 if ((X509Stack == NULL) || (Cert == NULL) || (CertSize == NULL)) {
252 return Status;
253 }
254
255 CertStack = (STACK_OF(X509) *) X509Stack;
256
257 X509Cert = sk_X509_pop (CertStack);
258
259 if (X509Cert == NULL) {
260 return Status;
261 }
262
263 Buffer = NULL;
264
265 CertBio = BIO_new (BIO_s_mem ());
266 if (CertBio == NULL) {
267 return Status;
268 }
269
270 Result = i2d_X509_bio (CertBio, X509Cert);
271 if (Result == 0) {
272 goto _Exit;
273 }
274
275 Length = ((BUF_MEM *) CertBio->ptr)->length;
276 if (Length <= 0) {
277 goto _Exit;
278 }
279
280 Buffer = malloc (Length);
281 if (Buffer == NULL) {
282 goto _Exit;
283 }
284
285 Result = BIO_read (CertBio, Buffer, Length);
286 if (Result != Length) {
287 goto _Exit;
288 }
289
290 *Cert = Buffer;
291 *CertSize = Length;
292
293 Status = TRUE;
294
295 _Exit:
296
297 BIO_free (CertBio);
298
299 if (!Status && (Buffer != NULL)) {
300 free (Buffer);
301 }
302
303 return Status;
304 }
305
306 /**
307 Get the signer's certificates from PKCS#7 signed data as described in "PKCS #7:
308 Cryptographic Message Syntax Standard". The input signed data could be wrapped
309 in a ContentInfo structure.
310
311 If P7Data, CertStack, StackLength, TrustedCert or CertLength is NULL, then
312 return FALSE. If P7Length overflow, then return FAlSE.
313
314 Caution: This function may receive untrusted input.
315 UEFI Authenticated Variable is external input, so this function will do basic
316 check for PKCS#7 data structure.
317
318 @param[in] P7Data Pointer to the PKCS#7 message to verify.
319 @param[in] P7Length Length of the PKCS#7 message in bytes.
320 @param[out] CertStack Pointer to Signer's certificates retrieved from P7Data.
321 It's caller's responsiblity to free the buffer.
322 @param[out] StackLength Length of signer's certificates in bytes.
323 @param[out] TrustedCert Pointer to a trusted certificate from Signer's certificates.
324 It's caller's responsiblity to free the buffer.
325 @param[out] CertLength Length of the trusted certificate in bytes.
326
327 @retval TRUE The operation is finished successfully.
328 @retval FALSE Error occurs during the operation.
329
330 **/
331 BOOLEAN
332 EFIAPI
333 Pkcs7GetSigners (
334 IN CONST UINT8 *P7Data,
335 IN UINTN P7Length,
336 OUT UINT8 **CertStack,
337 OUT UINTN *StackLength,
338 OUT UINT8 **TrustedCert,
339 OUT UINTN *CertLength
340 )
341 {
342 PKCS7 *Pkcs7;
343 BOOLEAN Status;
344 UINT8 *SignedData;
345 UINT8 *Temp;
346 UINTN SignedDataSize;
347 BOOLEAN Wrapped;
348 STACK_OF(X509) *Stack;
349 UINT8 Index;
350 UINT8 *CertBuf;
351 UINT8 *OldBuf;
352 UINTN BufferSize;
353 UINTN OldSize;
354 UINT8 *SingleCert;
355 UINTN SingleCertSize;
356
357 if ((P7Data == NULL) || (CertStack == NULL) || (StackLength == NULL) ||
358 (TrustedCert == NULL) || (CertLength == NULL) || (P7Length > INT_MAX)) {
359 return FALSE;
360 }
361
362 Status = WrapPkcs7Data (P7Data, P7Length, &Wrapped, &SignedData, &SignedDataSize);
363 if (!Status) {
364 return Status;
365 }
366
367 Status = FALSE;
368 Pkcs7 = NULL;
369 Stack = NULL;
370 CertBuf = NULL;
371 OldBuf = NULL;
372 SingleCert = NULL;
373
374 //
375 // Retrieve PKCS#7 Data (DER encoding)
376 //
377 if (SignedDataSize > INT_MAX) {
378 goto _Exit;
379 }
380
381 Temp = SignedData;
382 Pkcs7 = d2i_PKCS7 (NULL, (const unsigned char **) &Temp, (int) SignedDataSize);
383 if (Pkcs7 == NULL) {
384 goto _Exit;
385 }
386
387 //
388 // Check if it's PKCS#7 Signed Data (for Authenticode Scenario)
389 //
390 if (!PKCS7_type_is_signed (Pkcs7)) {
391 goto _Exit;
392 }
393
394 Stack = PKCS7_get0_signers(Pkcs7, NULL, PKCS7_BINARY);
395 if (Stack == NULL) {
396 goto _Exit;
397 }
398
399 //
400 // Convert CertStack to buffer in following format:
401 // UINT8 CertNumber;
402 // UINT32 Cert1Length;
403 // UINT8 Cert1[];
404 // UINT32 Cert2Length;
405 // UINT8 Cert2[];
406 // ...
407 // UINT32 CertnLength;
408 // UINT8 Certn[];
409 //
410 BufferSize = sizeof (UINT8);
411 OldSize = BufferSize;
412
413 for (Index = 0; ; Index++) {
414 Status = X509PopCertificate (Stack, &SingleCert, &SingleCertSize);
415 if (!Status) {
416 break;
417 }
418
419 OldSize = BufferSize;
420 OldBuf = CertBuf;
421 BufferSize = OldSize + SingleCertSize + sizeof (UINT32);
422 CertBuf = malloc (BufferSize);
423
424 if (CertBuf == NULL) {
425 goto _Exit;
426 }
427
428 if (OldBuf != NULL) {
429 CopyMem (CertBuf, OldBuf, OldSize);
430 free (OldBuf);
431 OldBuf = NULL;
432 }
433
434 WriteUnaligned32 ((UINT32 *) (CertBuf + OldSize), (UINT32) SingleCertSize);
435 CopyMem (CertBuf + OldSize + sizeof (UINT32), SingleCert, SingleCertSize);
436
437 free (SingleCert);
438 SingleCert = NULL;
439 }
440
441 if (CertBuf != NULL) {
442 //
443 // Update CertNumber.
444 //
445 CertBuf[0] = Index;
446
447 *CertLength = BufferSize - OldSize - sizeof (UINT32);
448 *TrustedCert = malloc (*CertLength);
449 if (*TrustedCert == NULL) {
450 goto _Exit;
451 }
452
453 CopyMem (*TrustedCert, CertBuf + OldSize + sizeof (UINT32), *CertLength);
454 *CertStack = CertBuf;
455 *StackLength = BufferSize;
456 Status = TRUE;
457 }
458
459 _Exit:
460 //
461 // Release Resources
462 //
463 if (!Wrapped) {
464 free (SignedData);
465 }
466
467 if (Pkcs7 != NULL) {
468 PKCS7_free (Pkcs7);
469 }
470
471 if (Stack != NULL) {
472 sk_X509_pop_free(Stack, X509_free);
473 }
474
475 if (SingleCert != NULL) {
476 free (SingleCert);
477 }
478
479 if (!Status && (CertBuf != NULL)) {
480 free (CertBuf);
481 *CertStack = NULL;
482 }
483
484 if (OldBuf != NULL) {
485 free (OldBuf);
486 }
487
488 return Status;
489 }
490
491 /**
492 Wrap function to use free() to free allocated memory for certificates.
493
494 @param[in] Certs Pointer to the certificates to be freed.
495
496 **/
497 VOID
498 EFIAPI
499 Pkcs7FreeSigners (
500 IN UINT8 *Certs
501 )
502 {
503 if (Certs == NULL) {
504 return;
505 }
506
507 free (Certs);
508 }
509
510 /**
511 Verifies the validility of a PKCS#7 signed data as described in "PKCS #7:
512 Cryptographic Message Syntax Standard". The input signed data could be wrapped
513 in a ContentInfo structure.
514
515 If P7Data, TrustedCert or InData is NULL, then return FALSE.
516 If P7Length, CertLength or DataLength overflow, then return FAlSE.
517
518 Caution: This function may receive untrusted input.
519 UEFI Authenticated Variable is external input, so this function will do basic
520 check for PKCS#7 data structure.
521
522 @param[in] P7Data Pointer to the PKCS#7 message to verify.
523 @param[in] P7Length Length of the PKCS#7 message in bytes.
524 @param[in] TrustedCert Pointer to a trusted/root certificate encoded in DER, which
525 is used for certificate chain verification.
526 @param[in] CertLength Length of the trusted certificate in bytes.
527 @param[in] InData Pointer to the content to be verified.
528 @param[in] DataLength Length of InData in bytes.
529
530 @retval TRUE The specified PKCS#7 signed data is valid.
531 @retval FALSE Invalid PKCS#7 signed data.
532
533 **/
534 BOOLEAN
535 EFIAPI
536 Pkcs7Verify (
537 IN CONST UINT8 *P7Data,
538 IN UINTN P7Length,
539 IN CONST UINT8 *TrustedCert,
540 IN UINTN CertLength,
541 IN CONST UINT8 *InData,
542 IN UINTN DataLength
543 )
544 {
545 PKCS7 *Pkcs7;
546 BIO *CertBio;
547 BIO *DataBio;
548 BOOLEAN Status;
549 X509 *Cert;
550 X509_STORE *CertStore;
551 UINT8 *SignedData;
552 UINT8 *Temp;
553 UINTN SignedDataSize;
554 BOOLEAN Wrapped;
555
556 //
557 // Check input parameters.
558 //
559 if (P7Data == NULL || TrustedCert == NULL || InData == NULL ||
560 P7Length > INT_MAX || CertLength > INT_MAX || DataLength > INT_MAX) {
561 return FALSE;
562 }
563
564 Pkcs7 = NULL;
565 CertBio = NULL;
566 DataBio = NULL;
567 Cert = NULL;
568 CertStore = NULL;
569
570 //
571 // Register & Initialize necessary digest algorithms for PKCS#7 Handling
572 //
573 if (EVP_add_digest (EVP_md5 ()) == 0) {
574 return FALSE;
575 }
576 if (EVP_add_digest (EVP_sha1 ()) == 0) {
577 return FALSE;
578 }
579 if (EVP_add_digest (EVP_sha256 ()) == 0) {
580 return FALSE;
581 }
582 if (EVP_add_digest_alias (SN_sha1WithRSAEncryption, SN_sha1WithRSA) == 0) {
583 return FALSE;
584 }
585
586
587 Status = WrapPkcs7Data (P7Data, P7Length, &Wrapped, &SignedData, &SignedDataSize);
588 if (!Status) {
589 return Status;
590 }
591
592 Status = FALSE;
593
594 //
595 // Retrieve PKCS#7 Data (DER encoding)
596 //
597 if (SignedDataSize > INT_MAX) {
598 goto _Exit;
599 }
600
601 Temp = SignedData;
602 Pkcs7 = d2i_PKCS7 (NULL, (const unsigned char **) &Temp, (int) SignedDataSize);
603 if (Pkcs7 == NULL) {
604 goto _Exit;
605 }
606
607 //
608 // Check if it's PKCS#7 Signed Data (for Authenticode Scenario)
609 //
610 if (!PKCS7_type_is_signed (Pkcs7)) {
611 goto _Exit;
612 }
613
614 //
615 // Read DER-encoded root certificate and Construct X509 Certificate
616 //
617 CertBio = BIO_new (BIO_s_mem ());
618 BIO_write (CertBio, TrustedCert, (int)CertLength);
619 if (CertBio == NULL) {
620 goto _Exit;
621 }
622 Cert = d2i_X509_bio (CertBio, NULL);
623 if (Cert == NULL) {
624 goto _Exit;
625 }
626
627 //
628 // Setup X509 Store for trusted certificate
629 //
630 CertStore = X509_STORE_new ();
631 if (CertStore == NULL) {
632 goto _Exit;
633 }
634 if (!(X509_STORE_add_cert (CertStore, Cert))) {
635 goto _Exit;
636 }
637
638 //
639 // Register customized X509 verification callback function to support
640 // trusted intermediate certificate anchor.
641 //
642 CertStore->verify_cb = X509VerifyCb;
643
644 //
645 // For generic PKCS#7 handling, InData may be NULL if the content is present
646 // in PKCS#7 structure. So ignore NULL checking here.
647 //
648 DataBio = BIO_new (BIO_s_mem ());
649 BIO_write (DataBio, InData, (int)DataLength);
650
651 //
652 // Verifies the PKCS#7 signedData structure
653 //
654 Status = (BOOLEAN) PKCS7_verify (Pkcs7, NULL, CertStore, DataBio, NULL, PKCS7_BINARY);
655
656 _Exit:
657 //
658 // Release Resources
659 //
660 BIO_free (DataBio);
661 BIO_free (CertBio);
662 X509_free (Cert);
663 X509_STORE_free (CertStore);
664 PKCS7_free (Pkcs7);
665
666 if (!Wrapped) {
667 OPENSSL_free (SignedData);
668 }
669
670 return Status;
671 }