2 * Copyright (C) 2010 Martin Willi
3 * Copyright (C) 2010 revosec AG
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>.
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
17 * Copyright (C) 2010 secunet Security Networks AG
18 * Copyright (C) 2010 Thomas Egerer
20 * Permission is hereby granted, free of charge, to any person obtaining a copy
21 * of this software and associated documentation files (the "Software"), to deal
22 * in the Software without restriction, including without limitation the rights
23 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
24 * copies of the Software, and to permit persons to whom the Software is
25 * furnished to do so, subject to the following conditions:
27 * The above copyright notice and this permission notice shall be included in
28 * all copies or substantial portions of the Software.
30 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
31 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
32 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
33 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
34 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
35 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
39 #include "openssl_crl.h"
40 #include "openssl_util.h"
42 #include <openssl/x509.h>
43 #include <openssl/x509v3.h>
45 #include <utils/debug.h>
46 #include <collections/enumerator.h>
47 #include <credentials/certificates/x509.h>
49 typedef struct private_openssl_crl_t private_openssl_crl_t
;
52 * Private data of an openssl_crl_t object.
54 struct private_openssl_crl_t
{
57 * Public openssl_crl_t interface.
62 * OpenSSL representation of a CRL
67 * DER encoding of the CRL
72 * Serial Number (crlNumber) of the CRL)
77 * AuthorityKeyIdentifier of the issuing CA
79 chunk_t authKeyIdentifier
;
87 * Date of next CRL expected
94 identification_t
*issuer
;
97 * Signature scheme used in this CRL
99 signature_scheme_t scheme
;
102 * References to this CRL
108 * Enumerator over revoked certificates
112 * Implements enumerator_t
117 * stack of revoked certificates
119 STACK_OF(X509_REVOKED
) *stack
;
122 * Total number of revoked certificates
127 * Current position of enumerator
133 METHOD(enumerator_t
, crl_enumerate
, bool,
134 crl_enumerator_t
*this, chunk_t
*serial
, time_t *date
, crl_reason_t
*reason
)
136 if (this->i
< this->num
)
138 X509_REVOKED
*revoked
;
139 ASN1_ENUMERATED
*crlrsn
;
141 revoked
= sk_X509_REVOKED_value(this->stack
, this->i
);
144 *serial
= openssl_asn1_str2chunk(revoked
->serialNumber
);
148 *date
= openssl_asn1_to_time(revoked
->revocationDate
);
152 *reason
= CRL_REASON_UNSPECIFIED
;
153 crlrsn
= X509_REVOKED_get_ext_d2i(revoked
, NID_crl_reason
,
157 if (ASN1_STRING_type(crlrsn
) == V_ASN1_ENUMERATED
&&
158 ASN1_STRING_length(crlrsn
) == 1)
160 *reason
= *ASN1_STRING_data(crlrsn
);
162 ASN1_STRING_free(crlrsn
);
171 METHOD(crl_t
, create_enumerator
, enumerator_t
*,
172 private_openssl_crl_t
*this)
174 crl_enumerator_t
*enumerator
;
178 .enumerate
= (void*)_crl_enumerate
,
179 .destroy
= (void*)free
,
181 .stack
= X509_CRL_get_REVOKED(this->crl
),
183 if (!enumerator
->stack
)
186 return enumerator_create_empty();
188 enumerator
->num
= sk_X509_REVOKED_num(enumerator
->stack
);
189 return &enumerator
->public;
192 METHOD(crl_t
, get_serial
, chunk_t
,
193 private_openssl_crl_t
*this)
198 METHOD(crl_t
, get_authKeyIdentifier
, chunk_t
,
199 private_openssl_crl_t
*this)
201 return this->authKeyIdentifier
;
204 METHOD(certificate_t
, get_type
, certificate_type_t
,
205 private_openssl_crl_t
*this)
207 return CERT_X509_CRL
;
210 METHOD(certificate_t
, get_subject_or_issuer
, identification_t
*,
211 private_openssl_crl_t
*this)
216 METHOD(certificate_t
, has_subject_or_issuer
, id_match_t
,
217 private_openssl_crl_t
*this, identification_t
*id
)
219 if (id
->get_type(id
) == ID_KEY_ID
&&
220 chunk_equals(this->authKeyIdentifier
, id
->get_encoding(id
)))
222 return ID_MATCH_PERFECT
;
224 return this->issuer
->matches(this->issuer
, id
);
227 METHOD(certificate_t
, issued_by
, bool,
228 private_openssl_crl_t
*this, certificate_t
*issuer
,
229 signature_scheme_t
*scheme
)
231 chunk_t fingerprint
, tbs
;
236 if (issuer
->get_type(issuer
) != CERT_X509
)
240 x509
= (x509_t
*)issuer
;
241 if (!(x509
->get_flags(x509
) & X509_CA
))
245 key
= issuer
->get_public_key(issuer
);
250 if (this->authKeyIdentifier
.ptr
&& key
)
252 if (!key
->get_fingerprint(key
, KEYID_PUBKEY_SHA1
, &fingerprint
) ||
253 !chunk_equals(fingerprint
, this->authKeyIdentifier
))
260 if (!this->issuer
->equals(this->issuer
, issuer
->get_subject(issuer
)))
265 if (this->scheme
== SIGN_UNKNOWN
)
269 tbs
= openssl_i2chunk(X509_CRL_INFO
, this->crl
->crl
);
270 valid
= key
->verify(key
, this->scheme
, tbs
,
271 openssl_asn1_str2chunk(this->crl
->signature
));
276 *scheme
= this->scheme
;
281 METHOD(certificate_t
, get_public_key
, public_key_t
*,
282 private_openssl_crl_t
*this)
287 METHOD(certificate_t
, get_validity
, bool,
288 private_openssl_crl_t
*this,
289 time_t *when
, time_t *not_before
, time_t *not_after
)
291 time_t t
= when
? *when
: time(NULL
);
295 *not_before
= this->thisUpdate
;
299 *not_after
= this->nextUpdate
;
301 return t
<= this->nextUpdate
;
304 METHOD(certificate_t
, get_encoding
, bool,
305 private_openssl_crl_t
*this, cred_encoding_type_t type
, chunk_t
*encoding
)
307 if (type
== CERT_ASN1_DER
)
309 *encoding
= chunk_clone(this->encoding
);
312 return lib
->encoding
->encode(lib
->encoding
, type
, NULL
, encoding
,
313 CRED_PART_X509_CRL_ASN1_DER
, this->encoding
, CRED_PART_END
);
316 METHOD(certificate_t
, equals
, bool,
317 private_openssl_crl_t
*this, certificate_t
*other
)
322 if (&this->public.crl
.certificate
== other
)
326 if (other
->equals
== (void*)equals
)
327 { /* skip allocation if we have the same implementation */
328 return chunk_equals(this->encoding
,
329 ((private_openssl_crl_t
*)other
)->encoding
);
331 if (!other
->get_encoding(other
, CERT_ASN1_DER
, &encoding
))
335 equal
= chunk_equals(this->encoding
, encoding
);
340 METHOD(certificate_t
, get_ref
, certificate_t
*,
341 private_openssl_crl_t
*this)
344 return &this->public.crl
.certificate
;
347 METHOD(certificate_t
, destroy
, void,
348 private_openssl_crl_t
*this)
350 if (ref_put(&this->ref
))
354 X509_CRL_free(this->crl
);
356 DESTROY_IF(this->issuer
);
357 free(this->authKeyIdentifier
.ptr
);
358 free(this->serial
.ptr
);
359 free(this->encoding
.ptr
);
365 * Create an empty CRL.
367 static private_openssl_crl_t
*create_empty()
369 private_openssl_crl_t
*this;
375 .get_type
= _get_type
,
376 .get_subject
= _get_subject_or_issuer
,
377 .get_issuer
= _get_subject_or_issuer
,
378 .has_subject
= _has_subject_or_issuer
,
379 .has_issuer
= _has_subject_or_issuer
,
380 .issued_by
= _issued_by
,
381 .get_public_key
= _get_public_key
,
382 .get_validity
= _get_validity
,
383 .get_encoding
= _get_encoding
,
388 .get_serial
= _get_serial
,
389 .get_authKeyIdentifier
= _get_authKeyIdentifier
,
390 .is_delta_crl
= (void*)return_false
,
391 .create_delta_crl_uri_enumerator
= (void*)enumerator_create_empty
,
392 .create_enumerator
= _create_enumerator
,
401 * Parse the authKeyIdentifier extension
403 static bool parse_authKeyIdentifier_ext(private_openssl_crl_t
*this,
406 AUTHORITY_KEYID
*keyid
;
408 keyid
= (AUTHORITY_KEYID
*)X509V3_EXT_d2i(ext
);
411 free(this->authKeyIdentifier
.ptr
);
412 this->authKeyIdentifier
= chunk_clone(
413 openssl_asn1_str2chunk(keyid
->keyid
));
414 AUTHORITY_KEYID_free(keyid
);
421 * Parse the crlNumber extension
423 static bool parse_crlNumber_ext(private_openssl_crl_t
*this,
428 chunk
= openssl_asn1_str2chunk(X509_EXTENSION_get_data(ext
));
429 /* quick and dirty INTEGER unwrap */
430 if (chunk
.len
> 1 && chunk
.ptr
[0] == V_ASN1_INTEGER
&&
431 chunk
.ptr
[1] == chunk
.len
- 2)
433 chunk
= chunk_skip(chunk
, 2);
434 free(this->serial
.ptr
);
435 this->serial
= chunk_clone(chunk
);
442 * Parse X509 CRL extensions
444 static bool parse_extensions(private_openssl_crl_t
*this)
449 STACK_OF(X509_EXTENSION
) *extensions
;
451 extensions
= this->crl
->crl
->extensions
;
454 num
= sk_X509_EXTENSION_num(extensions
);
455 for (i
= 0; i
< num
; ++i
)
457 ext
= sk_X509_EXTENSION_value(extensions
, i
);
459 switch (OBJ_obj2nid(X509_EXTENSION_get_object(ext
)))
461 case NID_authority_key_identifier
:
462 ok
= parse_authKeyIdentifier_ext(this, ext
);
465 ok
= parse_crlNumber_ext(this, ext
);
467 case NID_issuing_distribution_point
:
468 /* TODO support of IssuingDistributionPoints */
472 ok
= X509_EXTENSION_get_critical(ext
) == 0 ||
473 !lib
->settings
->get_bool(lib
->settings
,
474 "%s.x509.enforce_critical", TRUE
, lib
->ns
);
477 DBG1(DBG_LIB
, "found unsupported critical X.509 "
494 static bool parse_crl(private_openssl_crl_t
*this)
496 const unsigned char *ptr
= this->encoding
.ptr
;
498 this->crl
= d2i_X509_CRL(NULL
, &ptr
, this->encoding
.len
);
505 openssl_asn1_obj2chunk(this->crl
->crl
->sig_alg
->algorithm
),
506 openssl_asn1_obj2chunk(this->crl
->sig_alg
->algorithm
)))
510 this->scheme
= signature_scheme_from_oid(openssl_asn1_known_oid(
511 this->crl
->sig_alg
->algorithm
));
513 this->issuer
= openssl_x509_name2id(X509_CRL_get_issuer(this->crl
));
518 this->thisUpdate
= openssl_asn1_to_time(X509_CRL_get_lastUpdate(this->crl
));
519 this->nextUpdate
= openssl_asn1_to_time(X509_CRL_get_nextUpdate(this->crl
));
521 return parse_extensions(this);
527 openssl_crl_t
*openssl_crl_load(certificate_type_t type
, va_list args
)
529 chunk_t blob
= chunk_empty
;
533 switch (va_arg(args
, builder_part_t
))
535 case BUILD_BLOB_ASN1_DER
:
536 blob
= va_arg(args
, chunk_t
);
547 private_openssl_crl_t
*this = create_empty();
549 this->encoding
= chunk_clone(blob
);
552 return &this->public;