openssl: Disable PKCS#7/CMS when building against OpenSSL < 0.9.8g
[strongswan.git] / src / libstrongswan / plugins / openssl / openssl_pkcs7.c
1 /*
2 * Copyright (C) 2012 Martin Willi
3 * Copyright (C) 2012 revosec AG
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include <openssl/opensslv.h>
17 #include <openssl/opensslconf.h>
18
19 #if OPENSSL_VERSION_NUMBER >= 0x0090807fL
20 #ifndef OPENSSL_NO_CMS
21
22 #include "openssl_pkcs7.h"
23 #include "openssl_util.h"
24
25 #include <library.h>
26 #include <utils/debug.h>
27 #include <asn1/oid.h>
28 #include <credentials/sets/mem_cred.h>
29
30 #include <openssl/cms.h>
31
32 typedef struct private_openssl_pkcs7_t private_openssl_pkcs7_t;
33
34 /**
35 * Private data of an openssl_pkcs7_t object.
36 */
37 struct private_openssl_pkcs7_t {
38
39 /**
40 * Public pkcs7_t interface.
41 */
42 pkcs7_t public;
43
44 /**
45 * Type of this container
46 */
47 container_type_t type;
48
49 /**
50 * OpenSSL CMS structure
51 */
52 CMS_ContentInfo *cms;
53 };
54
55 /**
56 * OpenSSL does not allow us to read the signature to verify it with our own
57 * crypto API. We define the internal CMS_SignerInfo structure here to get it.
58 */
59 struct CMS_SignerInfo_st {
60 long version;
61 void *sid;
62 X509_ALGOR *digestAlgorithm;
63 STACK_OF(X509_ATTRIBUTE) *signedAttrs;
64 X509_ALGOR *signatureAlgorithm;
65 ASN1_OCTET_STRING *signature;
66 /* and more... */
67 };
68
69 /**
70 * And we also need access to the wrappend CMS_KeyTransRecipientInfo to
71 * read the encrypted key
72 */
73 struct CMS_KeyTransRecipientInfo_st {
74 long version;
75 void *rid;
76 X509_ALGOR *keyEncryptionAlgorithm;
77 ASN1_OCTET_STRING *encryptedKey;
78 };
79
80 struct CMS_RecipientInfo_st {
81 int type;
82 struct CMS_KeyTransRecipientInfo_st *ktri;
83 /* and more in union... */
84 };
85
86 struct CMS_EncryptedContentInfo_st {
87 ASN1_OBJECT *contentType;
88 X509_ALGOR *contentEncryptionAlgorithm;
89 ASN1_OCTET_STRING *encryptedContent;
90 /* and more... */
91 };
92
93 struct CMS_EnvelopedData_st {
94 long version;
95 void *originatorInfo;
96 STACK_OF(CMS_RecipientInfo) *recipientInfos;
97 struct CMS_EncryptedContentInfo_st *encryptedContentInfo;
98 /* and more... */
99 };
100
101 struct CMS_ContentInfo_st {
102 ASN1_OBJECT *contentType;
103 struct CMS_EnvelopedData_st *envelopedData;
104 /* and more in union... */
105 };
106
107 /**
108 * We can't include asn1.h, declare function prototypes directly
109 */
110 chunk_t asn1_wrap(int, const char *mode, ...);
111 int asn1_unwrap(chunk_t*, chunk_t*);
112
113 /**
114 * Enumerator over certificates
115 */
116 typedef struct {
117 /** implements enumerator_t */
118 enumerator_t public;
119 /** Stack of X509 certificates */
120 STACK_OF(X509) *certs;
121 /** current enumerator position in certificates */
122 int i;
123 /** currently enumerating certificate_t */
124 certificate_t *cert;
125 } cert_enumerator_t;
126
127 METHOD(enumerator_t, cert_destroy, void,
128 cert_enumerator_t *this)
129 {
130 DESTROY_IF(this->cert);
131 free(this);
132 }
133
134 METHOD(enumerator_t, cert_enumerate, bool,
135 cert_enumerator_t *this, certificate_t **out)
136 {
137 if (!this->certs)
138 {
139 return FALSE;
140 }
141 while (this->i < sk_X509_num(this->certs))
142 {
143 chunk_t encoding;
144 X509 *x509;
145
146 /* clean up previous round */
147 DESTROY_IF(this->cert);
148 this->cert = NULL;
149
150 x509 = sk_X509_value(this->certs, this->i++);
151 encoding = openssl_i2chunk(X509, x509);
152 this->cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
153 BUILD_BLOB_ASN1_DER, encoding,
154 BUILD_END);
155 free(encoding.ptr);
156 if (!this->cert)
157 {
158 continue;
159 }
160 *out = this->cert;
161 return TRUE;
162 }
163 return FALSE;
164 }
165
166 METHOD(pkcs7_t, create_cert_enumerator, enumerator_t*,
167 private_openssl_pkcs7_t *this)
168 {
169 cert_enumerator_t *enumerator;
170
171 if (this->type == CONTAINER_PKCS7_SIGNED_DATA)
172 {
173 INIT(enumerator,
174 .public = {
175 .enumerate = (void*)_cert_enumerate,
176 .destroy = _cert_destroy,
177 },
178 .certs = CMS_get1_certs(this->cms),
179 );
180 return &enumerator->public;
181 }
182 return enumerator_create_empty();
183 }
184
185 /**
186 * Enumerator for signatures
187 */
188 typedef struct {
189 /** implements enumerator_t */
190 enumerator_t public;
191 /** Stack of signerinfos */
192 STACK_OF(CMS_SignerInfo) *signers;
193 /** current enumerator position in signers */
194 int i;
195 /** currently enumerating auth config */
196 auth_cfg_t *auth;
197 /** full CMS */
198 CMS_ContentInfo *cms;
199 /** credential set containing wrapped certificates */
200 mem_cred_t *creds;
201 } signature_enumerator_t;
202
203 /**
204 * Verify signerInfo signature
205 */
206 static auth_cfg_t *verify_signature(CMS_SignerInfo *si, int hash_oid)
207 {
208 enumerator_t *enumerator;
209 public_key_t *key;
210 certificate_t *cert;
211 auth_cfg_t *auth, *found = NULL;
212 identification_t *issuer, *serial;
213 chunk_t attrs = chunk_empty, sig, attr;
214 X509_NAME *name;
215 ASN1_INTEGER *snr;
216 int i;
217
218 if (CMS_SignerInfo_get0_signer_id(si, NULL, &name, &snr) != 1)
219 {
220 return NULL;
221 }
222 issuer = openssl_x509_name2id(name);
223 if (!issuer)
224 {
225 return NULL;
226 }
227 serial = identification_create_from_encoding(
228 ID_KEY_ID, openssl_asn1_str2chunk(snr));
229
230 /* reconstruct DER encoded attributes to verify signature */
231 for (i = 0; i < CMS_signed_get_attr_count(si); i++)
232 {
233 attr = openssl_i2chunk(X509_ATTRIBUTE, CMS_signed_get_attr(si, i));
234 attrs = chunk_cat("mm", attrs, attr);
235 }
236 /* wrap in a ASN1_SET */
237 attrs = asn1_wrap(0x31, "m", attrs);
238
239 /* TODO: find a better way to access and verify the signature */
240 sig = openssl_asn1_str2chunk(si->signature);
241 enumerator = lib->credmgr->create_trusted_enumerator(lib->credmgr,
242 KEY_RSA, serial, FALSE);
243 while (enumerator->enumerate(enumerator, &cert, &auth))
244 {
245 if (issuer->equals(issuer, cert->get_issuer(cert)))
246 {
247 key = cert->get_public_key(cert);
248 if (key)
249 {
250 if (key->verify(key, signature_scheme_from_oid(hash_oid),
251 attrs, sig))
252 {
253 found = auth->clone(auth);
254 key->destroy(key);
255 break;
256 }
257 key->destroy(key);
258 }
259 }
260 }
261 enumerator->destroy(enumerator);
262 issuer->destroy(issuer);
263 serial->destroy(serial);
264 free(attrs.ptr);
265
266 return found;
267 }
268
269 /**
270 * Verify the message digest in the signerInfo attributes
271 */
272 static bool verify_digest(CMS_ContentInfo *cms, CMS_SignerInfo *si, int hash_oid)
273 {
274 ASN1_OCTET_STRING *os, **osp;
275 hash_algorithm_t hash_alg;
276 chunk_t digest, content, hash;
277 hasher_t *hasher;
278
279 os = CMS_signed_get0_data_by_OBJ(si,
280 OBJ_nid2obj(NID_pkcs9_messageDigest), -3, V_ASN1_OCTET_STRING);
281 if (!os)
282 {
283 return FALSE;
284 }
285 digest = openssl_asn1_str2chunk(os);
286 osp = CMS_get0_content(cms);
287 if (!osp)
288 {
289 return FALSE;
290 }
291 content = openssl_asn1_str2chunk(*osp);
292
293 hash_alg = hasher_algorithm_from_oid(hash_oid);
294 hasher = lib->crypto->create_hasher(lib->crypto, hash_alg);
295 if (!hasher)
296 {
297 DBG1(DBG_LIB, "hash algorithm %N not supported",
298 hash_algorithm_names, hash_alg);
299 return FALSE;
300 }
301 if (!hasher->allocate_hash(hasher, content, &hash))
302 {
303 hasher->destroy(hasher);
304 return FALSE;
305 }
306 hasher->destroy(hasher);
307
308 if (!chunk_equals(digest, hash))
309 {
310 free(hash.ptr);
311 DBG1(DBG_LIB, "invalid messageDigest");
312 return FALSE;
313 }
314 free(hash.ptr);
315 return TRUE;
316 }
317
318 METHOD(enumerator_t, signature_enumerate, bool,
319 signature_enumerator_t *this, auth_cfg_t **out)
320 {
321 if (!this->signers)
322 {
323 return FALSE;
324 }
325 while (this->i < sk_CMS_SignerInfo_num(this->signers))
326 {
327 CMS_SignerInfo *si;
328 X509_ALGOR *digest, *sig;
329 int hash_oid;
330
331 /* clean up previous round */
332 DESTROY_IF(this->auth);
333 this->auth = NULL;
334
335 si = sk_CMS_SignerInfo_value(this->signers, this->i++);
336
337 CMS_SignerInfo_get0_algs(si, NULL, NULL, &digest, &sig);
338 hash_oid = openssl_asn1_known_oid(digest->algorithm);
339 if (openssl_asn1_known_oid(sig->algorithm) != OID_RSA_ENCRYPTION)
340 {
341 DBG1(DBG_LIB, "only RSA digest encryption supported");
342 continue;
343 }
344 this->auth = verify_signature(si, hash_oid);
345 if (!this->auth)
346 {
347 DBG1(DBG_LIB, "unable to verify pkcs7 attributes signature");
348 continue;
349 }
350 if (!verify_digest(this->cms, si, hash_oid))
351 {
352 continue;
353 }
354 *out = this->auth;
355 return TRUE;
356 }
357 return FALSE;
358 }
359
360 METHOD(enumerator_t, signature_destroy, void,
361 signature_enumerator_t *this)
362 {
363 lib->credmgr->remove_local_set(lib->credmgr, &this->creds->set);
364 this->creds->destroy(this->creds);
365 DESTROY_IF(this->auth);
366 free(this);
367 }
368
369 METHOD(container_t, create_signature_enumerator, enumerator_t*,
370 private_openssl_pkcs7_t *this)
371 {
372 signature_enumerator_t *enumerator;
373
374 if (this->type == CONTAINER_PKCS7_SIGNED_DATA)
375 {
376 enumerator_t *certs;
377 certificate_t *cert;
378
379 INIT(enumerator,
380 .public = {
381 .enumerate = (void*)_signature_enumerate,
382 .destroy = _signature_destroy,
383 },
384 .cms = this->cms,
385 .signers = CMS_get0_SignerInfos(this->cms),
386 .creds = mem_cred_create(),
387 );
388
389 /* make available wrapped certs during signature checking */
390 certs = create_cert_enumerator(this);
391 while (certs->enumerate(certs, &cert))
392 {
393 enumerator->creds->add_cert(enumerator->creds, FALSE,
394 cert->get_ref(cert));
395 }
396 certs->destroy(certs);
397
398 lib->credmgr->add_local_set(lib->credmgr, &enumerator->creds->set,
399 FALSE);
400
401 return &enumerator->public;
402 }
403 return enumerator_create_empty();
404 }
405
406
407 METHOD(container_t, get_type, container_type_t,
408 private_openssl_pkcs7_t *this)
409 {
410 return this->type;
411 }
412
413 METHOD(pkcs7_t, get_attribute, bool,
414 private_openssl_pkcs7_t *this, int oid,
415 enumerator_t *enumerator, chunk_t *value)
416 {
417 signature_enumerator_t *e;
418 CMS_SignerInfo *si;
419 X509_ATTRIBUTE *attr;
420 ASN1_TYPE *type;
421 chunk_t chunk, wrapped;
422 int i;
423
424 e = (signature_enumerator_t*)enumerator;
425 if (e->i <= 0)
426 {
427 return FALSE;
428 }
429
430 /* "i" gets incremeneted after enumerate(), hence read from previous */
431 si = sk_CMS_SignerInfo_value(e->signers, e->i - 1);
432 for (i = 0; i < CMS_signed_get_attr_count(si); i++)
433 {
434 attr = CMS_signed_get_attr(si, i);
435 if (!attr->single && sk_ASN1_TYPE_num(attr->value.set) == 1 &&
436 openssl_asn1_known_oid(attr->object) == oid)
437 {
438 /* get first value in SET */
439 type = sk_ASN1_TYPE_value(attr->value.set, 0);
440 chunk = wrapped = openssl_i2chunk(ASN1_TYPE, type);
441 if (asn1_unwrap(&chunk, &chunk) != 0x100 /* ASN1_INVALID */)
442 {
443 *value = chunk_clone(chunk);
444 free(wrapped.ptr);
445 return TRUE;
446 }
447 free(wrapped.ptr);
448 }
449 }
450 return FALSE;
451 }
452
453 /**
454 * Find a private key for issuerAndSerialNumber
455 */
456 static private_key_t *find_private(identification_t *issuer,
457 identification_t *serial)
458 {
459 enumerator_t *enumerator;
460 certificate_t *cert;
461 public_key_t *public;
462 private_key_t *private = NULL;
463 identification_t *id;
464 chunk_t fp;
465
466 enumerator = lib->credmgr->create_cert_enumerator(lib->credmgr,
467 CERT_X509, KEY_RSA, serial, FALSE);
468 while (enumerator->enumerate(enumerator, &cert))
469 {
470 if (issuer->equals(issuer, cert->get_issuer(cert)))
471 {
472 public = cert->get_public_key(cert);
473 if (public)
474 {
475 if (public->get_fingerprint(public, KEYID_PUBKEY_SHA1, &fp))
476 {
477 id = identification_create_from_encoding(ID_KEY_ID, fp);
478 private = lib->credmgr->get_private(lib->credmgr,
479 KEY_ANY, id, NULL);
480 id->destroy(id);
481 }
482 public->destroy(public);
483 }
484 }
485 if (private)
486 {
487 break;
488 }
489 }
490 enumerator->destroy(enumerator);
491 return private;
492 }
493
494 /**
495 * Decrypt enveloped-data with a decrypted symmetric key
496 */
497 static bool decrypt_symmetric(private_openssl_pkcs7_t *this, chunk_t key,
498 chunk_t encrypted, chunk_t *plain)
499 {
500 encryption_algorithm_t encr;
501 X509_ALGOR *alg;
502 crypter_t *crypter;
503 chunk_t iv;
504 size_t key_size;
505
506 /* read encryption algorithm from interal structures; TODO fixup */
507 alg = this->cms->envelopedData->encryptedContentInfo->
508 contentEncryptionAlgorithm;
509 encr = encryption_algorithm_from_oid(openssl_asn1_known_oid(alg->algorithm),
510 &key_size);
511 if (alg->parameter->type != V_ASN1_OCTET_STRING)
512 {
513 return FALSE;
514 }
515 iv = openssl_asn1_str2chunk(alg->parameter->value.octet_string);
516
517 crypter = lib->crypto->create_crypter(lib->crypto, encr, key_size / 8);
518 if (!crypter)
519 {
520 DBG1(DBG_LIB, "crypter %N-%d not available",
521 encryption_algorithm_names, alg, key_size);
522 return FALSE;
523 }
524 if (key.len != crypter->get_key_size(crypter))
525 {
526 DBG1(DBG_LIB, "symmetric key length is wrong");
527 crypter->destroy(crypter);
528 return FALSE;
529 }
530 if (iv.len != crypter->get_iv_size(crypter))
531 {
532 DBG1(DBG_LIB, "IV length is wrong");
533 crypter->destroy(crypter);
534 return FALSE;
535 }
536 if (!crypter->set_key(crypter, key) ||
537 !crypter->decrypt(crypter, encrypted, iv, plain))
538 {
539 crypter->destroy(crypter);
540 return FALSE;
541 }
542 crypter->destroy(crypter);
543 return TRUE;
544 }
545
546 /**
547 * Remove enveloped-data PKCS#7 padding from plain data
548 */
549 static bool remove_padding(chunk_t *data)
550 {
551 u_char *pos;
552 u_char pattern;
553 size_t padding;
554
555 if (!data->len)
556 {
557 return FALSE;
558 }
559 pos = data->ptr + data->len - 1;
560 padding = pattern = *pos;
561
562 if (padding > data->len)
563 {
564 DBG1(DBG_LIB, "padding greater than data length");
565 return FALSE;
566 }
567 data->len -= padding;
568
569 while (padding-- > 0)
570 {
571 if (*pos-- != pattern)
572 {
573 DBG1(DBG_LIB, "wrong padding pattern");
574 return FALSE;
575 }
576 }
577 return TRUE;
578 }
579
580 /**
581 * Decrypt PKCS#7 enveloped-data
582 */
583 static bool decrypt(private_openssl_pkcs7_t *this,
584 chunk_t encrypted, chunk_t *plain)
585 {
586 STACK_OF(CMS_RecipientInfo) *ris;
587 CMS_RecipientInfo *ri;
588 chunk_t chunk, key = chunk_empty;
589 int i;
590
591 ris = CMS_get0_RecipientInfos(this->cms);
592 for (i = 0; i < sk_CMS_RecipientInfo_num(ris); i++)
593 {
594 ri = sk_CMS_RecipientInfo_value(ris, i);
595 if (CMS_RecipientInfo_type(ri) == CMS_RECIPINFO_TRANS)
596 {
597 identification_t *serial, *issuer;
598 private_key_t *private;
599 X509_ALGOR *alg;
600 X509_NAME *name;
601 ASN1_INTEGER *sn;
602 u_char zero = 0;
603 int oid;
604
605 if (CMS_RecipientInfo_ktri_get0_algs(ri, NULL, NULL, &alg) == 1 &&
606 CMS_RecipientInfo_ktri_get0_signer_id(ri, NULL, &name, &sn) == 1)
607 {
608 oid = openssl_asn1_known_oid(alg->algorithm);
609 if (oid != OID_RSA_ENCRYPTION)
610 {
611 DBG1(DBG_LIB, "only RSA encryption supported in PKCS#7");
612 continue;
613 }
614 issuer = openssl_x509_name2id(name);
615 if (!issuer)
616 {
617 continue;
618 }
619 chunk = openssl_asn1_str2chunk(sn);
620 if (chunk.len && chunk.ptr[0] & 0x80)
621 { /* if MSB is set, append a zero to make it non-negative */
622 chunk = chunk_cata("cc", chunk_from_thing(zero), chunk);
623 }
624 serial = identification_create_from_encoding(ID_KEY_ID, chunk);
625 private = find_private(issuer, serial);
626 issuer->destroy(issuer);
627 serial->destroy(serial);
628
629 if (private)
630 {
631 /* get encryptedKey from internal structure; TODO fixup */
632 chunk = openssl_asn1_str2chunk(ri->ktri->encryptedKey);
633 if (private->decrypt(private, ENCRYPT_RSA_PKCS1,
634 chunk, &key))
635 {
636 private->destroy(private);
637 break;
638 }
639 private->destroy(private);
640 }
641 }
642 }
643 }
644 if (!key.len)
645 {
646 DBG1(DBG_LIB, "no private key found to decrypt PKCS#7");
647 return FALSE;
648 }
649 if (!decrypt_symmetric(this, key, encrypted, plain))
650 {
651 chunk_clear(&key);
652 return FALSE;
653 }
654 chunk_clear(&key);
655 if (!remove_padding(plain))
656 {
657 free(plain->ptr);
658 return FALSE;
659 }
660 return TRUE;
661 }
662
663 METHOD(container_t, get_data, bool,
664 private_openssl_pkcs7_t *this, chunk_t *data)
665 {
666 ASN1_OCTET_STRING **os;
667 chunk_t chunk;
668
669 os = CMS_get0_content(this->cms);
670 if (os)
671 {
672 chunk = openssl_asn1_str2chunk(*os);
673 switch (this->type)
674 {
675 case CONTAINER_PKCS7_DATA:
676 case CONTAINER_PKCS7_SIGNED_DATA:
677 *data = chunk_clone(chunk);
678 return TRUE;
679 case CONTAINER_PKCS7_ENVELOPED_DATA:
680 return decrypt(this, chunk, data);
681 default:
682 break;
683 }
684 }
685 return FALSE;
686 }
687
688 METHOD(container_t, get_encoding, bool,
689 private_openssl_pkcs7_t *this, chunk_t *data)
690 {
691 return FALSE;
692 }
693
694 METHOD(container_t, destroy, void,
695 private_openssl_pkcs7_t *this)
696 {
697 CMS_ContentInfo_free(this->cms);
698 free(this);
699 }
700
701 /**
702 * Generic constructor
703 */
704 static private_openssl_pkcs7_t* create_empty()
705 {
706 private_openssl_pkcs7_t *this;
707
708 INIT(this,
709 .public = {
710 .container = {
711 .get_type = _get_type,
712 .create_signature_enumerator = _create_signature_enumerator,
713 .get_data = _get_data,
714 .get_encoding = _get_encoding,
715 .destroy = _destroy,
716 },
717 .get_attribute = _get_attribute,
718 .create_cert_enumerator = _create_cert_enumerator,
719 },
720 );
721
722 return this;
723 }
724
725 /**
726 * Parse a PKCS#7 container
727 */
728 static bool parse(private_openssl_pkcs7_t *this, chunk_t blob)
729 {
730 BIO *bio;
731
732 bio = BIO_new_mem_buf(blob.ptr, blob.len);
733 this->cms = d2i_CMS_bio(bio, NULL);
734 BIO_free(bio);
735
736 if (!this->cms)
737 {
738 return FALSE;
739 }
740 switch (openssl_asn1_known_oid((ASN1_OBJECT*)CMS_get0_type(this->cms)))
741 {
742 case OID_PKCS7_DATA:
743 this->type = CONTAINER_PKCS7_DATA;
744 break;
745 case OID_PKCS7_SIGNED_DATA:
746 this->type = CONTAINER_PKCS7_SIGNED_DATA;
747 break;
748 case OID_PKCS7_ENVELOPED_DATA:
749 this->type = CONTAINER_PKCS7_ENVELOPED_DATA;
750 break;
751 default:
752 return FALSE;
753 }
754
755 return TRUE;
756 }
757
758 /**
759 * See header
760 */
761 pkcs7_t *openssl_pkcs7_load(container_type_t type, va_list args)
762 {
763 chunk_t blob = chunk_empty;
764 private_openssl_pkcs7_t *this;
765
766 while (TRUE)
767 {
768 switch (va_arg(args, builder_part_t))
769 {
770 case BUILD_BLOB_ASN1_DER:
771 blob = va_arg(args, chunk_t);
772 continue;
773 case BUILD_END:
774 break;
775 default:
776 return NULL;
777 }
778 break;
779 }
780 if (blob.len)
781 {
782 this = create_empty();
783 if (parse(this, blob))
784 {
785 return &this->public;
786 }
787 destroy(this);
788 }
789 return NULL;
790 }
791
792 #endif /* OPENSSL_NO_CMS */
793 #endif /* OPENSSL_VERSION_NUMBER */