summaryrefslogtreecommitdiff
path: root/src/libstrongswan/plugins/openssl/openssl_crl.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libstrongswan/plugins/openssl/openssl_crl.c')
-rw-r--r--src/libstrongswan/plugins/openssl/openssl_crl.c530
1 files changed, 530 insertions, 0 deletions
diff --git a/src/libstrongswan/plugins/openssl/openssl_crl.c b/src/libstrongswan/plugins/openssl/openssl_crl.c
new file mode 100644
index 000000000..5645d72d7
--- /dev/null
+++ b/src/libstrongswan/plugins/openssl/openssl_crl.c
@@ -0,0 +1,530 @@
+/*
+ * Copyright (C) 2010 Martin Willi
+ * Copyright (C) 2010 revosec AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+/*
+ * Copyright (C) 2010 secunet Security Networks AG
+ * Copyright (C) 2010 Thomas Egerer
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "openssl_crl.h"
+#include "openssl_util.h"
+
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+
+#include <debug.h>
+#include <utils/enumerator.h>
+#include <credentials/certificates/x509.h>
+
+typedef struct private_openssl_crl_t private_openssl_crl_t;
+
+/**
+ * Private data of an openssl_crl_t object.
+ */
+struct private_openssl_crl_t {
+
+ /**
+ * Public openssl_crl_t interface.
+ */
+ openssl_crl_t public;
+
+ /**
+ * OpenSSL representation of a CRL
+ */
+ X509_CRL *crl;
+
+ /**
+ * DER encoding of the CRL
+ */
+ chunk_t encoding;
+
+ /**
+ * Serial Number (crlNumber) of the CRL)
+ */
+ chunk_t serial;
+
+ /**
+ * AuthorityKeyIdentifier of the issuing CA
+ */
+ chunk_t authKeyIdentifier;
+
+ /**
+ * Date of this CRL
+ */
+ time_t thisUpdate;
+
+ /**
+ * Date of next CRL expected
+ */
+ time_t nextUpdate;
+
+ /**
+ * Issuer of this CRL
+ */
+ identification_t *issuer;
+
+ /**
+ * Signature scheme used in this CRL
+ */
+ signature_scheme_t scheme;
+
+ /**
+ * References to this CRL
+ */
+ refcount_t ref;
+};
+
+/**
+ * Enumerator over revoked certificates
+ */
+typedef struct {
+ /**
+ * Implements enumerator_t
+ */
+ enumerator_t public;
+
+ /**
+ * stack of revoked certificates
+ */
+ STACK_OF(X509_REVOKED) *stack;
+
+ /**
+ * Total number of revoked certificates
+ */
+ int num;
+
+ /**
+ * Current position of enumerator
+ */
+ int i;
+} crl_enumerator_t;
+
+
+METHOD(enumerator_t, crl_enumerate, bool,
+ crl_enumerator_t *this, chunk_t *serial, time_t *date, crl_reason_t *reason)
+{
+ if (this->i < this->num)
+ {
+ X509_REVOKED *revoked;
+ ASN1_ENUMERATED *crlrsn;
+
+ revoked = sk_X509_REVOKED_value(this->stack, this->i);
+ if (serial)
+ {
+ *serial = openssl_asn1_str2chunk(revoked->serialNumber);
+ }
+ if (date)
+ {
+ *date = openssl_asn1_to_time(revoked->revocationDate);
+ }
+ if (reason)
+ {
+ *reason = CRL_REASON_UNSPECIFIED;
+ crlrsn = X509_REVOKED_get_ext_d2i(revoked, NID_crl_reason,
+ NULL, NULL);
+ if (crlrsn)
+ {
+ if (ASN1_STRING_type(crlrsn) == V_ASN1_ENUMERATED &&
+ ASN1_STRING_length(crlrsn) == 1)
+ {
+ *reason = *ASN1_STRING_data(crlrsn);
+ }
+ ASN1_STRING_free(crlrsn);
+ }
+ }
+ this->i++;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+METHOD(crl_t, create_enumerator, enumerator_t*,
+ private_openssl_crl_t *this)
+{
+ crl_enumerator_t *enumerator;
+
+ INIT(enumerator,
+ .public = {
+ .enumerate = (void*)_crl_enumerate,
+ .destroy = (void*)free,
+ },
+ .stack = X509_CRL_get_REVOKED(this->crl),
+ );
+ if (!enumerator->stack)
+ {
+ free(enumerator);
+ return enumerator_create_empty();
+ }
+ enumerator->num = sk_X509_EXTENSION_num(enumerator->stack);
+ return &enumerator->public;
+}
+
+METHOD(crl_t, get_serial, chunk_t,
+ private_openssl_crl_t *this)
+{
+ return this->serial;
+}
+
+METHOD(crl_t, get_authKeyIdentifier, chunk_t,
+ private_openssl_crl_t *this)
+{
+ return this->authKeyIdentifier;
+}
+
+METHOD(certificate_t, get_type, certificate_type_t,
+ private_openssl_crl_t *this)
+{
+ return CERT_X509_CRL;
+}
+
+METHOD(certificate_t, get_subject_or_issuer, identification_t*,
+ private_openssl_crl_t *this)
+{
+ return this->issuer;
+}
+
+METHOD(certificate_t, has_subject_or_issuer, id_match_t,
+ private_openssl_crl_t *this, identification_t *id)
+{
+ if (id->get_type(id) == ID_KEY_ID &&
+ chunk_equals(this->authKeyIdentifier, id->get_encoding(id)))
+ {
+ return ID_MATCH_PERFECT;
+ }
+ return this->issuer->matches(this->issuer, id);
+}
+
+METHOD(certificate_t, issued_by, bool,
+ private_openssl_crl_t *this, certificate_t *issuer)
+{
+ chunk_t fingerprint, tbs;
+ public_key_t *key;
+ x509_t *x509;
+ bool valid;
+
+ if (issuer->get_type(issuer) != CERT_X509)
+ {
+ return FALSE;
+ }
+ x509 = (x509_t*)issuer;
+ if (!(x509->get_flags(x509) & X509_CA))
+ {
+ return FALSE;
+ }
+ key = issuer->get_public_key(issuer);
+ if (!key)
+ {
+ return FALSE;
+ }
+ if (this->authKeyIdentifier.ptr && key)
+ {
+ if (!key->get_fingerprint(key, KEYID_PUBKEY_SHA1, &fingerprint) ||
+ !chunk_equals(fingerprint, this->authKeyIdentifier))
+ {
+ return FALSE;
+ }
+ }
+ else
+ {
+ if (!this->issuer->equals(this->issuer, issuer->get_subject(issuer)))
+ {
+ return FALSE;
+ }
+ }
+ if (this->scheme == SIGN_UNKNOWN)
+ {
+ return FALSE;
+ }
+ tbs = openssl_i2chunk(X509_CRL_INFO, this->crl->crl);
+ valid = key->verify(key, this->scheme, tbs,
+ openssl_asn1_str2chunk(this->crl->signature));
+ free(tbs.ptr);
+ key->destroy(key);
+ return valid;
+}
+
+METHOD(certificate_t, get_public_key, public_key_t*,
+ private_openssl_crl_t *this)
+{
+ return NULL;
+}
+
+METHOD(certificate_t, get_validity, bool,
+ private_openssl_crl_t *this,
+ time_t *when, time_t *not_before, time_t *not_after)
+{
+ time_t t = when ? *when : time(NULL);
+
+ if (not_before)
+ {
+ *not_before = this->thisUpdate;
+ }
+ if (not_after)
+ {
+ *not_after = this->nextUpdate;
+ }
+ return t <= this->nextUpdate;
+}
+
+METHOD(certificate_t, get_encoding, bool,
+ private_openssl_crl_t *this, cred_encoding_type_t type, chunk_t *encoding)
+{
+ if (type == CERT_ASN1_DER)
+ {
+ *encoding = chunk_clone(this->encoding);
+ return TRUE;
+ }
+ return lib->encoding->encode(lib->encoding, type, NULL, encoding,
+ CRED_PART_X509_CRL_ASN1_DER, this->encoding, CRED_PART_END);
+}
+
+METHOD(certificate_t, equals, bool,
+ private_openssl_crl_t *this, certificate_t *other)
+{
+ chunk_t encoding;
+ bool equal;
+
+ if (&this->public.crl.certificate == other)
+ {
+ return TRUE;
+ }
+ if (other->equals == (void*)equals)
+ { /* skip allocation if we have the same implementation */
+ return chunk_equals(this->encoding,
+ ((private_openssl_crl_t*)other)->encoding);
+ }
+ if (!other->get_encoding(other, CERT_ASN1_DER, &encoding))
+ {
+ return FALSE;
+ }
+ equal = chunk_equals(this->encoding, encoding);
+ free(encoding.ptr);
+ return equal;
+}
+
+METHOD(certificate_t, get_ref, certificate_t*,
+ private_openssl_crl_t *this)
+{
+ ref_get(&this->ref);
+ return &this->public.crl.certificate;
+}
+
+METHOD(certificate_t, destroy, void,
+ private_openssl_crl_t *this)
+{
+ if (ref_put(&this->ref))
+ {
+ if (this->crl)
+ {
+ X509_CRL_free(this->crl);
+ }
+ DESTROY_IF(this->issuer);
+ free(this->authKeyIdentifier.ptr);
+ free(this->serial.ptr);
+ free(this->encoding.ptr);
+ free(this);
+ }
+}
+
+/**
+ * Create an empty CRL.
+ */
+static private_openssl_crl_t *create_empty()
+{
+ private_openssl_crl_t *this;
+
+ INIT(this,
+ .public = {
+ .crl = {
+ .certificate = {
+ .get_type = _get_type,
+ .get_subject = _get_subject_or_issuer,
+ .get_issuer = _get_subject_or_issuer,
+ .has_subject = _has_subject_or_issuer,
+ .has_issuer = _has_subject_or_issuer,
+ .issued_by = _issued_by,
+ .get_public_key = _get_public_key,
+ .get_validity = _get_validity,
+ .get_encoding = _get_encoding,
+ .equals = _equals,
+ .get_ref = _get_ref,
+ .destroy = _destroy,
+ },
+ .get_serial = _get_serial,
+ .get_authKeyIdentifier = _get_authKeyIdentifier,
+ .create_enumerator = _create_enumerator,
+ },
+ },
+ .ref = 1,
+ );
+ return this;
+}
+
+/**
+ * Parse the authKeyIdentifier extension
+ */
+static bool parse_authKeyIdentifier_ext(private_openssl_crl_t *this,
+ X509_EXTENSION *ext)
+{
+ AUTHORITY_KEYID *keyid;
+
+ keyid = (AUTHORITY_KEYID *)X509V3_EXT_d2i(ext);
+ if (keyid)
+ {
+ free(this->authKeyIdentifier.ptr);
+ this->authKeyIdentifier = chunk_clone(
+ openssl_asn1_str2chunk(keyid->keyid));
+ AUTHORITY_KEYID_free(keyid);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/**
+ * Parse the crlNumber extension
+ */
+static bool parse_crlNumber_ext(private_openssl_crl_t *this,
+ X509_EXTENSION *ext)
+{
+ free(this->serial.ptr);
+ this->serial = chunk_clone(
+ openssl_asn1_str2chunk(X509_EXTENSION_get_data(ext)));
+ return this->serial.len != 0;
+}
+
+/**
+ * Parse X509 CRL extensions
+ */
+static bool parse_extensions(private_openssl_crl_t *this)
+{
+ bool ok;
+ int i, num;
+ X509_EXTENSION *ext;
+ STACK_OF(X509_EXTENSION) *extensions;
+
+ extensions = this->crl->crl->extensions;
+ if (extensions)
+ {
+ num = sk_X509_EXTENSION_num(extensions);
+ for (i = 0; i < num; ++i)
+ {
+ ext = sk_X509_EXTENSION_value(extensions, i);
+
+ switch (OBJ_obj2nid(X509_EXTENSION_get_object(ext)))
+ {
+ case NID_authority_key_identifier:
+ ok = parse_authKeyIdentifier_ext(this, ext);
+ break;
+ case NID_crl_number:
+ ok = parse_crlNumber_ext(this, ext);
+ break;
+ default:
+ ok = TRUE;
+ break;
+ }
+ if (!ok)
+ {
+ return FALSE;
+ }
+ }
+ }
+ return TRUE;
+}
+
+/**
+ * Parse a X509 CRL
+ */
+static bool parse_crl(private_openssl_crl_t *this)
+{
+ const unsigned char *ptr = this->encoding.ptr;
+
+ this->crl = d2i_X509_CRL(NULL, &ptr, this->encoding.len);
+ if (!this->crl)
+ {
+ return FALSE;
+ }
+
+ if (!chunk_equals(
+ openssl_asn1_obj2chunk(this->crl->crl->sig_alg->algorithm),
+ openssl_asn1_obj2chunk(this->crl->sig_alg->algorithm)))
+ {
+ return FALSE;
+ }
+ this->scheme = signature_scheme_from_oid(openssl_asn1_known_oid(
+ this->crl->sig_alg->algorithm));
+
+ this->issuer = openssl_x509_name2id(X509_CRL_get_issuer(this->crl));
+ if (!this->issuer)
+ {
+ return FALSE;
+ }
+ this->thisUpdate = openssl_asn1_to_time(X509_CRL_get_lastUpdate(this->crl));
+ this->nextUpdate = openssl_asn1_to_time(X509_CRL_get_nextUpdate(this->crl));
+
+ return parse_extensions(this);
+}
+
+/**
+ * Load the CRL.
+ */
+openssl_crl_t *openssl_crl_load(certificate_type_t type, va_list args)
+{
+ chunk_t blob = chunk_empty;
+
+ while (TRUE)
+ {
+ switch (va_arg(args, builder_part_t))
+ {
+ case BUILD_BLOB_ASN1_DER:
+ blob = va_arg(args, chunk_t);
+ continue;
+ case BUILD_END:
+ break;
+ default:
+ return NULL;
+ }
+ break;
+ }
+ if (blob.ptr)
+ {
+ private_openssl_crl_t *this = create_empty();
+
+ this->encoding = chunk_clone(blob);
+ if (parse_crl(this))
+ {
+ return &this->public;
+ }
+ destroy(this);
+ }
+ return NULL;
+}