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