ARG_DISBL_SET([gmp], [disable GNU MP (libgmp) based crypto implementation plugin.])
ARG_DISBL_SET([random], [disable RNG implementation on top of /dev/(u)random.])
ARG_DISBL_SET([x509], [disable X509 certificate implementation plugin.])
+ARG_DISBL_SET([revocation], [disable X509 CRL/OCSP revocation check plugin.])
ARG_DISBL_SET([pubkey], [disable RAW public key support plugin.])
ARG_DISBL_SET([pkcs1], [disable PKCS1 key decoding plugin.])
ARG_DISBL_SET([pgp], [disable PGP key decoding plugin.])
libstrongswan_plugins=${libstrongswan_plugins}" x509"
pluto_plugins=${pluto_plugins}" x509"
fi
+if test x$revocation = xtrue; then
+ libstrongswan_plugins=${libstrongswan_plugins}" revocation"
+fi
if test x$pubkey = xtrue; then
libstrongswan_plugins=${libstrongswan_plugins}" pubkey"
pluto_plugins=${pluto_plugins}" pubkey"
AM_CONDITIONAL(USE_GMP, test x$gmp = xtrue)
AM_CONDITIONAL(USE_RANDOM, test x$random = xtrue)
AM_CONDITIONAL(USE_X509, test x$x509 = xtrue)
+AM_CONDITIONAL(USE_REVOCATION, test x$revocation = xtrue)
AM_CONDITIONAL(USE_PUBKEY, test x$pubkey = xtrue)
AM_CONDITIONAL(USE_PKCS1, test x$pkcs1 = xtrue)
AM_CONDITIONAL(USE_PGP, test x$pgp = xtrue)
src/libstrongswan/plugins/hmac/Makefile
src/libstrongswan/plugins/xcbc/Makefile
src/libstrongswan/plugins/x509/Makefile
+ src/libstrongswan/plugins/revocation/Makefile
src/libstrongswan/plugins/pubkey/Makefile
src/libstrongswan/plugins/pkcs1/Makefile
src/libstrongswan/plugins/pgp/Makefile
endif
endif
+if USE_REVOCATION
+ SUBDIRS += plugins/revocation
+if MONOLITHIC
+ libstrongswan_la_LIBADD += plugins/revocation/libstrongswan-revocation.la
+endif
+endif
+
if USE_PUBKEY
SUBDIRS += plugins/pubkey
if MONOLITHIC
#include <threading/thread_value.h>
#include <threading/mutex.h>
#include <threading/rwlock.h>
-#include <selectors/traffic_selector.h>
#include <utils/linked_list.h>
#include <credentials/sets/cert_cache.h>
#include <credentials/sets/auth_cfg_wrapper.h>
-#include <credentials/sets/ocsp_response_wrapper.h>
#include <credentials/certificates/x509.h>
-#include <credentials/certificates/crl.h>
-#include <credentials/certificates/ocsp_request.h>
-#include <credentials/certificates/ocsp_response.h>
/**
* Maximum length of a certificate trust chain
}
/**
- * forward declaration
- */
-static enumerator_t *create_trusted_enumerator(private_credential_manager_t *this,
- key_type_t type, identification_t *id, bool online);
-
-/**
- * Do an OCSP request
- */
-static certificate_t *fetch_ocsp(private_credential_manager_t *this, char *url,
- certificate_t *subject, certificate_t *issuer)
-{
- certificate_t *request, *response;
- chunk_t send, receive;
-
- /* TODO: requestor name, signature */
- request = lib->creds->create(lib->creds,
- CRED_CERTIFICATE, CERT_X509_OCSP_REQUEST,
- BUILD_CA_CERT, issuer,
- BUILD_CERT, subject, BUILD_END);
- if (!request)
- {
- DBG1(DBG_CFG, "generating ocsp request failed");
- return NULL;
- }
-
- send = request->get_encoding(request);
- request->destroy(request);
-
- DBG1(DBG_CFG, " requesting ocsp status from '%s' ...", url);
- if (lib->fetcher->fetch(lib->fetcher, url, &receive,
- FETCH_REQUEST_DATA, send,
- FETCH_REQUEST_TYPE, "application/ocsp-request",
- FETCH_END) != SUCCESS)
- {
- DBG1(DBG_CFG, "ocsp request to %s failed", url);
- chunk_free(&send);
- return NULL;
- }
- chunk_free(&send);
-
- response = lib->creds->create(lib->creds,
- CRED_CERTIFICATE, CERT_X509_OCSP_RESPONSE,
- BUILD_BLOB_ASN1_DER, receive, BUILD_END);
- chunk_free(&receive);
- if (!response)
- {
- DBG1(DBG_CFG, "parsing ocsp response failed");
- return NULL;
- }
- return response;
-}
-
-/**
- * check the signature of an OCSP response
- */
-static bool verify_ocsp(private_credential_manager_t *this,
- ocsp_response_t *response)
-{
- certificate_t *issuer, *subject;
- identification_t *responder;
- ocsp_response_wrapper_t *wrapper;
- enumerator_t *enumerator;
- bool verified = FALSE;
-
- wrapper = ocsp_response_wrapper_create((ocsp_response_t*)response);
- add_local_set(this, &wrapper->set);
-
- subject = &response->certificate;
- responder = subject->get_issuer(subject);
- enumerator = create_trusted_enumerator(this, KEY_ANY, responder, FALSE);
- while (enumerator->enumerate(enumerator, &issuer, NULL))
- {
- if (this->cache->issued_by(this->cache, subject, issuer))
- {
- DBG1(DBG_CFG, " ocsp response correctly signed by \"%Y\"",
- issuer->get_subject(issuer));
- verified = TRUE;
- break;
- }
- }
- enumerator->destroy(enumerator);
-
- remove_local_set(this, &wrapper->set);
- wrapper->destroy(wrapper);
- return verified;
-}
-
-/**
- * Get the better of two OCSP responses, and check for usable OCSP info
- */
-static certificate_t *get_better_ocsp(private_credential_manager_t *this,
- certificate_t *cand, certificate_t *best,
- x509_t *subject, x509_t *issuer,
- cert_validation_t *valid, bool cache)
-{
- ocsp_response_t *response;
- time_t revocation, this_update, next_update, valid_until;
- crl_reason_t reason;
- bool revoked = FALSE;
-
- response = (ocsp_response_t*)cand;
-
- /* check ocsp signature */
- if (!verify_ocsp(this, response))
- {
- DBG1(DBG_CFG, "ocsp response verification failed");
- cand->destroy(cand);
- return best;
- }
- /* check if response contains our certificate */
- switch (response->get_status(response, subject, issuer, &revocation, &reason,
- &this_update, &next_update))
- {
- case VALIDATION_REVOKED:
- /* subject has been revoked by a valid OCSP response */
- DBG1(DBG_CFG, "certificate was revoked on %T, reason: %N",
- &revocation, TRUE, crl_reason_names, reason);
- revoked = TRUE;
- break;
- case VALIDATION_GOOD:
- /* results in either good or stale */
- break;
- default:
- case VALIDATION_FAILED:
- /* candidate unusable, does not contain our cert */
- DBG1(DBG_CFG, " ocsp response contains no status on our certificate");
- cand->destroy(cand);
- return best;
- }
-
- /* select the better of the two responses */
- if (best == NULL || certificate_is_newer(cand, best))
- {
- DESTROY_IF(best);
- best = cand;
- if (best->get_validity(best, NULL, NULL, &valid_until))
- {
- DBG1(DBG_CFG, " ocsp response is valid: until %T",
- &valid_until, FALSE);
- *valid = VALIDATION_GOOD;
- if (cache)
- { /* cache non-stale only, stale certs get refetched */
- cache_cert(this, best);
- }
- }
- else
- {
- DBG1(DBG_CFG, " ocsp response is stale: since %T",
- &valid_until, FALSE);
- *valid = VALIDATION_STALE;
- }
- }
- else
- {
- *valid = VALIDATION_STALE;
- cand->destroy(cand);
- }
- if (revoked)
- { /* revoked always counts, even if stale */
- *valid = VALIDATION_REVOKED;
- }
- return best;
-}
-
-/**
- * validate a x509 certificate using OCSP
- */
-static cert_validation_t check_ocsp(private_credential_manager_t *this,
- x509_t *subject, x509_t *issuer,
- auth_cfg_t *auth)
-{
- enumerator_t *enumerator;
- cert_validation_t valid = VALIDATION_SKIPPED;
- certificate_t *best = NULL, *current;
- identification_t *keyid = NULL;
- public_key_t *public;
- chunk_t chunk;
- char *uri = NULL;
-
- /** lookup cache for valid OCSP responses */
- enumerator = create_cert_enumerator(this, CERT_X509_OCSP_RESPONSE,
- KEY_ANY, NULL, FALSE);
- while (enumerator->enumerate(enumerator, ¤t))
- {
- current->get_ref(current);
- best = get_better_ocsp(this, current, best, subject, issuer,
- &valid, FALSE);
- if (best && valid != VALIDATION_STALE)
- {
- DBG1(DBG_CFG, " using cached ocsp response");
- break;
- }
- }
- enumerator->destroy(enumerator);
-
- /* derive the authorityKeyIdentifier from the issuer's public key */
- current = &issuer->interface;
- public = current->get_public_key(current);
- if (public && public->get_fingerprint(public, KEY_ID_PUBKEY_SHA1, &chunk))
- {
- keyid = identification_create_from_encoding(ID_KEY_ID, chunk);
- }
- /** fetch from configured OCSP responder URLs */
- if (keyid && valid != VALIDATION_GOOD && valid != VALIDATION_REVOKED)
- {
- enumerator = create_cdp_enumerator(this, CERT_X509_OCSP_RESPONSE, keyid);
- while (enumerator->enumerate(enumerator, &uri))
- {
- current = fetch_ocsp(this, uri, &subject->interface,
- &issuer->interface);
- if (current)
- {
- best = get_better_ocsp(this, current, best, subject, issuer,
- &valid, TRUE);
- if (best && valid != VALIDATION_STALE)
- {
- break;
- }
- }
- }
- enumerator->destroy(enumerator);
- }
- DESTROY_IF(public);
- DESTROY_IF(keyid);
-
- /* fallback to URL fetching from subject certificate's URIs */
- if (valid != VALIDATION_GOOD && valid != VALIDATION_REVOKED)
- {
- enumerator = subject->create_ocsp_uri_enumerator(subject);
- while (enumerator->enumerate(enumerator, &uri))
- {
- current = fetch_ocsp(this, uri, &subject->interface,
- &issuer->interface);
- if (current)
- {
- best = get_better_ocsp(this, current, best, subject, issuer,
- &valid, TRUE);
- if (best && valid != VALIDATION_STALE)
- {
- break;
- }
- }
- }
- enumerator->destroy(enumerator);
- }
- /* an uri was found, but no result. switch validation state to failed */
- if (valid == VALIDATION_SKIPPED && uri)
- {
- valid = VALIDATION_FAILED;
- }
- if (auth)
- {
- auth->add(auth, AUTH_RULE_OCSP_VALIDATION, valid);
- if (valid == VALIDATION_GOOD)
- { /* successful OCSP check fulfills also CRL constraint */
- auth->add(auth, AUTH_RULE_CRL_VALIDATION, VALIDATION_GOOD);
- }
- }
- DESTROY_IF(best);
- return valid;
-}
-
-/**
- * fetch a CRL from an URL
- */
-static certificate_t* fetch_crl(private_credential_manager_t *this, char *url)
-{
- certificate_t *crl;
- chunk_t chunk;
-
- DBG1(DBG_CFG, " fetching crl from '%s' ...", url);
- if (lib->fetcher->fetch(lib->fetcher, url, &chunk, FETCH_END) != SUCCESS)
- {
- DBG1(DBG_CFG, "crl fetching failed");
- return NULL;
- }
- crl = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509_CRL,
- BUILD_BLOB_ASN1_DER, chunk, BUILD_END);
- chunk_free(&chunk);
- if (!crl)
- {
- DBG1(DBG_CFG, "crl fetched successfully but parsing failed");
- return NULL;
- }
- return crl;
-}
-
-/**
- * check the signature of an CRL
- */
-static bool verify_crl(private_credential_manager_t *this, certificate_t *crl)
-{
- certificate_t *issuer;
- enumerator_t *enumerator;
- bool verified = FALSE;
-
- enumerator = create_trusted_enumerator(this, KEY_ANY, crl->get_issuer(crl),
- FALSE);
- while (enumerator->enumerate(enumerator, &issuer, NULL))
- {
- if (this->cache->issued_by(this->cache, crl, issuer))
- {
- DBG1(DBG_CFG, " crl correctly signed by \"%Y\"",
- issuer->get_subject(issuer));
- verified = TRUE;
- break;
- }
- }
- enumerator->destroy(enumerator);
-
- return verified;
-}
-
-/**
- * Get the better of two CRLs, and check for usable CRL info
- */
-static certificate_t *get_better_crl(private_credential_manager_t *this,
- certificate_t *cand, certificate_t *best,
- x509_t *subject, x509_t *issuer,
- cert_validation_t *valid, bool cache)
-{
- enumerator_t *enumerator;
- time_t revocation, valid_until;
- crl_reason_t reason;
- chunk_t serial;
- crl_t *crl;
-
- /* check CRL signature */
- if (!verify_crl(this, cand))
- {
- DBG1(DBG_CFG, "crl response verification failed");
- cand->destroy(cand);
- return best;
- }
-
- crl = (crl_t*)cand;
- enumerator = crl->create_enumerator(crl);
- while (enumerator->enumerate(enumerator, &serial, &revocation, &reason))
- {
- if (chunk_equals(serial, subject->get_serial(subject)))
- {
- DBG1(DBG_CFG, "certificate was revoked on %T, reason: %N",
- &revocation, TRUE, crl_reason_names, reason);
- *valid = VALIDATION_REVOKED;
- enumerator->destroy(enumerator);
- DESTROY_IF(best);
- return cand;
- }
- }
- enumerator->destroy(enumerator);
-
- /* select the better of the two CRLs */
- if (best == NULL || crl_is_newer(crl, (crl_t*)best))
- {
- DESTROY_IF(best);
- best = cand;
- if (best->get_validity(best, NULL, NULL, &valid_until))
- {
- DBG1(DBG_CFG, " crl is valid: until %T", &valid_until, FALSE);
- *valid = VALIDATION_GOOD;
- if (cache)
- { /* we cache non-stale crls only, as a stale crls are refetched */
- cache_cert(this, best);
- }
- }
- else
- {
- DBG1(DBG_CFG, " crl is stale: since %T", &valid_until, FALSE);
- *valid = VALIDATION_STALE;
- }
- }
- else
- {
- *valid = VALIDATION_STALE;
- cand->destroy(cand);
- }
- return best;
-}
-
-/**
- * validate a x509 certificate using CRL
- */
-static cert_validation_t check_crl(private_credential_manager_t *this,
- x509_t *subject, x509_t *issuer,
- auth_cfg_t *auth)
-{
- cert_validation_t valid = VALIDATION_SKIPPED;
- identification_t *keyid = NULL;
- certificate_t *best = NULL;
- certificate_t *current;
- public_key_t *public;
- enumerator_t *enumerator;
- chunk_t chunk;
- char *uri = NULL;
-
- /* derive the authorityKeyIdentifier from the issuer's public key */
- current = &issuer->interface;
- public = current->get_public_key(current);
- if (public && public->get_fingerprint(public, KEY_ID_PUBKEY_SHA1, &chunk))
- {
- keyid = identification_create_from_encoding(ID_KEY_ID, chunk);
-
- /* find a cached crl by authorityKeyIdentifier */
- enumerator = create_cert_enumerator(this, CERT_X509_CRL, KEY_ANY,
- keyid, FALSE);
- while (enumerator->enumerate(enumerator, ¤t))
- {
- current->get_ref(current);
- best = get_better_crl(this, current, best, subject, issuer,
- &valid, FALSE);
- if (best && valid != VALIDATION_STALE)
- {
- DBG1(DBG_CFG, " using cached crl");
- break;
- }
- }
- enumerator->destroy(enumerator);
-
- /* fallback to fetching crls from credential sets cdps */
- if (valid != VALIDATION_GOOD && valid != VALIDATION_REVOKED)
- {
- enumerator = create_cdp_enumerator(this, CERT_X509_CRL, keyid);
-
- while (enumerator->enumerate(enumerator, &uri))
- {
- current = fetch_crl(this, uri);
- if (current)
- {
- best = get_better_crl(this, current, best, subject, issuer,
- &valid, TRUE);
- if (best && valid != VALIDATION_STALE)
- {
- break;
- }
- }
- }
- enumerator->destroy(enumerator);
- }
- keyid->destroy(keyid);
- }
- DESTROY_IF(public);
-
- /* fallback to fetching crls from cdps from subject's certificate */
- if (valid != VALIDATION_GOOD && valid != VALIDATION_REVOKED)
- {
- enumerator = subject->create_crl_uri_enumerator(subject);
-
- while (enumerator->enumerate(enumerator, &uri))
- {
- current = fetch_crl(this, uri);
- if (current)
- {
- best = get_better_crl(this, current, best, subject, issuer,
- &valid, TRUE);
- if (best && valid != VALIDATION_STALE)
- {
- break;
- }
- }
- }
- enumerator->destroy(enumerator);
- }
-
- /* an uri was found, but no result. switch validation state to failed */
- if (valid == VALIDATION_SKIPPED && uri)
- {
- valid = VALIDATION_FAILED;
- }
- if (auth)
- {
- if (valid == VALIDATION_SKIPPED)
- { /* if we skipped CRL validation, we use the result of OCSP for
- * constraint checking */
- auth->add(auth, AUTH_RULE_CRL_VALIDATION,
- auth->get(auth, AUTH_RULE_OCSP_VALIDATION));
- }
- else
- {
- auth->add(auth, AUTH_RULE_CRL_VALIDATION, valid);
- }
- }
- DESTROY_IF(best);
- return valid;
-}
-
-/**
* check a certificate for its lifetime
*/
static bool check_certificate(private_credential_manager_t *this,
pathlen, pathlen_constraint);
return FALSE;
}
-
- if (online)
- {
- DBG1(DBG_CFG, "checking certificate status of \"%Y\"",
- subject->get_subject(subject));
- switch (check_ocsp(this, (x509_t*)subject, (x509_t*)issuer, auth))
- {
- case VALIDATION_GOOD:
- DBG1(DBG_CFG, "certificate status is good");
- return TRUE;
- case VALIDATION_REVOKED:
- /* has already been logged */
- return FALSE;
- case VALIDATION_SKIPPED:
- DBG2(DBG_CFG, "ocsp check skipped, no ocsp found");
- break;
- case VALIDATION_STALE:
- DBG1(DBG_CFG, "ocsp information stale, fallback to crl");
- break;
- case VALIDATION_FAILED:
- DBG1(DBG_CFG, "ocsp check failed, fallback to crl");
- break;
- }
- switch (check_crl(this, (x509_t*)subject, (x509_t*)issuer, auth))
- {
- case VALIDATION_GOOD:
- DBG1(DBG_CFG, "certificate status is good");
- return TRUE;
- case VALIDATION_REVOKED:
- /* has already been logged */
- return FALSE;
- case VALIDATION_FAILED:
- case VALIDATION_SKIPPED:
- DBG1(DBG_CFG, "certificate status is not available");
- break;
- case VALIDATION_STALE:
- DBG1(DBG_CFG, "certificate status is unknown, crl is stale");
- break;
- }
- }
}
enumerator = this->validators->create_enumerator(this->validators);
--- /dev/null
+
+INCLUDES = -I$(top_srcdir)/src/libstrongswan
+
+AM_CFLAGS = -rdynamic
+
+if MONOLITHIC
+noinst_LTLIBRARIES = libstrongswan-revocation.la
+else
+plugin_LTLIBRARIES = libstrongswan-revocation.la
+endif
+
+libstrongswan_revocation_la_SOURCES = \
+ revocation_plugin.h revocation_plugin.c \
+ revocation_validator.h revocation_validator.c
+
+libstrongswan_revocation_la_LDFLAGS = -module -avoid-version
--- /dev/null
+/*
+ * 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.
+ */
+
+#include "revocation_plugin.h"
+
+#include <library.h>
+#include "revocation_validator.h"
+
+typedef struct private_revocation_plugin_t private_revocation_plugin_t;
+
+/**
+ * private data of revocation_plugin
+ */
+struct private_revocation_plugin_t {
+
+ /**
+ * public functions
+ */
+ revocation_plugin_t public;
+
+ /**
+ * Validator implementation instance.
+ */
+ revocation_validator_t *validator;
+};
+
+METHOD(plugin_t, destroy, void,
+ private_revocation_plugin_t *this)
+{
+ lib->credmgr->remove_validator(lib->credmgr, &this->validator->validator);
+ this->validator->destroy(this->validator);
+ free(this);
+}
+
+/*
+ * see header file
+ */
+plugin_t *revocation_plugin_create()
+{
+ private_revocation_plugin_t *this;
+
+ INIT(this,
+ .public.plugin.destroy = _destroy,
+ .validator = revocation_validator_create(),
+ );
+ lib->credmgr->add_validator(lib->credmgr, &this->validator->validator);
+
+ return &this->public.plugin;
+}
--- /dev/null
+/*
+ * 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.
+ */
+
+/**
+ * @defgroup revocation revocation
+ * @ingroup plugins
+ *
+ * @defgroup revocation_plugin revocation_plugin
+ * @{ @ingroup revocation
+ */
+
+#ifndef REVOCATION_PLUGIN_H_
+#define REVOCATION_PLUGIN_H_
+
+#include <plugins/plugin.h>
+
+typedef struct revocation_plugin_t revocation_plugin_t;
+
+/**
+ * X509 certificate revocation support using CRL and OCSP.
+ */
+struct revocation_plugin_t {
+
+ /**
+ * Implements plugin_t. interface.
+ */
+ plugin_t plugin;
+};
+
+#endif /** REVOCATION_PLUGIN_H_ @}*/
--- /dev/null
+/*
+ * Copyright (C) 2010 Martin Willi
+ * Copyright (C) 2010 revosec AG
+ * Copyright (C) 2009 Andreas Steffen
+ * Hochschule fuer Technik Rapperswil
+ *
+ * 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.
+ */
+
+#include "revocation_validator.h"
+
+#include <debug.h>
+#include <credentials/certificates/x509.h>
+#include <credentials/certificates/crl.h>
+#include <credentials/certificates/ocsp_request.h>
+#include <credentials/certificates/ocsp_response.h>
+#include <credentials/sets/ocsp_response_wrapper.h>
+#include <selectors/traffic_selector.h>
+
+typedef struct private_revocation_validator_t private_revocation_validator_t;
+
+/**
+ * Private data of an revocation_validator_t object.
+ */
+struct private_revocation_validator_t {
+
+ /**
+ * Public revocation_validator_t interface.
+ */
+ revocation_validator_t public;
+};
+
+/**
+ * Do an OCSP request
+ */
+static certificate_t *fetch_ocsp(char *url, certificate_t *subject,
+ certificate_t *issuer)
+{
+ certificate_t *request, *response;
+ chunk_t send, receive;
+
+ /* TODO: requestor name, signature */
+ request = lib->creds->create(lib->creds,
+ CRED_CERTIFICATE, CERT_X509_OCSP_REQUEST,
+ BUILD_CA_CERT, issuer,
+ BUILD_CERT, subject, BUILD_END);
+ if (!request)
+ {
+ DBG1(DBG_CFG, "generating ocsp request failed");
+ return NULL;
+ }
+
+ send = request->get_encoding(request);
+ request->destroy(request);
+
+ DBG1(DBG_CFG, " requesting ocsp status from '%s' ...", url);
+ if (lib->fetcher->fetch(lib->fetcher, url, &receive,
+ FETCH_REQUEST_DATA, send,
+ FETCH_REQUEST_TYPE, "application/ocsp-request",
+ FETCH_END) != SUCCESS)
+ {
+ DBG1(DBG_CFG, "ocsp request to %s failed", url);
+ chunk_free(&send);
+ return NULL;
+ }
+ chunk_free(&send);
+
+ response = lib->creds->create(lib->creds,
+ CRED_CERTIFICATE, CERT_X509_OCSP_RESPONSE,
+ BUILD_BLOB_ASN1_DER, receive, BUILD_END);
+ chunk_free(&receive);
+ if (!response)
+ {
+ DBG1(DBG_CFG, "parsing ocsp response failed");
+ return NULL;
+ }
+ return response;
+}
+
+/**
+ * check the signature of an OCSP response
+ */
+static bool verify_ocsp(ocsp_response_t *response)
+{
+ certificate_t *issuer, *subject;
+ identification_t *responder;
+ ocsp_response_wrapper_t *wrapper;
+ enumerator_t *enumerator;
+ bool verified = FALSE;
+
+ wrapper = ocsp_response_wrapper_create((ocsp_response_t*)response);
+ lib->credmgr->add_local_set(lib->credmgr, &wrapper->set);
+
+ subject = &response->certificate;
+ responder = subject->get_issuer(subject);
+ enumerator = lib->credmgr->create_trusted_enumerator(lib->credmgr,
+ KEY_ANY, responder, FALSE);
+ while (enumerator->enumerate(enumerator, &issuer, NULL))
+ {
+ if (lib->credmgr->issued_by(lib->credmgr, subject, issuer))
+ {
+ DBG1(DBG_CFG, " ocsp response correctly signed by \"%Y\"",
+ issuer->get_subject(issuer));
+ verified = TRUE;
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+
+ lib->credmgr->remove_local_set(lib->credmgr, &wrapper->set);
+ wrapper->destroy(wrapper);
+ return verified;
+}
+
+/**
+ * Get the better of two OCSP responses, and check for usable OCSP info
+ */
+static certificate_t *get_better_ocsp(certificate_t *cand, certificate_t *best,
+ x509_t *subject, x509_t *issuer, cert_validation_t *valid, bool cache)
+{
+ ocsp_response_t *response;
+ time_t revocation, this_update, next_update, valid_until;
+ crl_reason_t reason;
+ bool revoked = FALSE;
+
+ response = (ocsp_response_t*)cand;
+
+ /* check ocsp signature */
+ if (!verify_ocsp(response))
+ {
+ DBG1(DBG_CFG, "ocsp response verification failed");
+ cand->destroy(cand);
+ return best;
+ }
+ /* check if response contains our certificate */
+ switch (response->get_status(response, subject, issuer, &revocation, &reason,
+ &this_update, &next_update))
+ {
+ case VALIDATION_REVOKED:
+ /* subject has been revoked by a valid OCSP response */
+ DBG1(DBG_CFG, "certificate was revoked on %T, reason: %N",
+ &revocation, TRUE, crl_reason_names, reason);
+ revoked = TRUE;
+ break;
+ case VALIDATION_GOOD:
+ /* results in either good or stale */
+ break;
+ default:
+ case VALIDATION_FAILED:
+ /* candidate unusable, does not contain our cert */
+ DBG1(DBG_CFG, " ocsp response contains no status on our certificate");
+ cand->destroy(cand);
+ return best;
+ }
+
+ /* select the better of the two responses */
+ if (best == NULL || certificate_is_newer(cand, best))
+ {
+ DESTROY_IF(best);
+ best = cand;
+ if (best->get_validity(best, NULL, NULL, &valid_until))
+ {
+ DBG1(DBG_CFG, " ocsp response is valid: until %T",
+ &valid_until, FALSE);
+ *valid = VALIDATION_GOOD;
+ if (cache)
+ { /* cache non-stale only, stale certs get refetched */
+ lib->credmgr->cache_cert(lib->credmgr, best);
+ }
+ }
+ else
+ {
+ DBG1(DBG_CFG, " ocsp response is stale: since %T",
+ &valid_until, FALSE);
+ *valid = VALIDATION_STALE;
+ }
+ }
+ else
+ {
+ *valid = VALIDATION_STALE;
+ cand->destroy(cand);
+ }
+ if (revoked)
+ { /* revoked always counts, even if stale */
+ *valid = VALIDATION_REVOKED;
+ }
+ return best;
+}
+
+/**
+ * validate a x509 certificate using OCSP
+ */
+static cert_validation_t check_ocsp(x509_t *subject, x509_t *issuer,
+ auth_cfg_t *auth)
+{
+ enumerator_t *enumerator;
+ cert_validation_t valid = VALIDATION_SKIPPED;
+ certificate_t *best = NULL, *current;
+ identification_t *keyid = NULL;
+ public_key_t *public;
+ chunk_t chunk;
+ char *uri = NULL;
+
+ /** lookup cache for valid OCSP responses */
+ enumerator = lib->credmgr->create_cert_enumerator(lib->credmgr,
+ CERT_X509_OCSP_RESPONSE, KEY_ANY, NULL, FALSE);
+ while (enumerator->enumerate(enumerator, ¤t))
+ {
+ current->get_ref(current);
+ best = get_better_ocsp(current, best, subject, issuer, &valid, FALSE);
+ if (best && valid != VALIDATION_STALE)
+ {
+ DBG1(DBG_CFG, " using cached ocsp response");
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+
+ /* derive the authorityKeyIdentifier from the issuer's public key */
+ current = &issuer->interface;
+ public = current->get_public_key(current);
+ if (public && public->get_fingerprint(public, KEY_ID_PUBKEY_SHA1, &chunk))
+ {
+ keyid = identification_create_from_encoding(ID_KEY_ID, chunk);
+ }
+ /** fetch from configured OCSP responder URLs */
+ if (keyid && valid != VALIDATION_GOOD && valid != VALIDATION_REVOKED)
+ {
+ enumerator = lib->credmgr->create_cdp_enumerator(lib->credmgr,
+ CERT_X509_OCSP_RESPONSE, keyid);
+ while (enumerator->enumerate(enumerator, &uri))
+ {
+ current = fetch_ocsp(uri, &subject->interface, &issuer->interface);
+ if (current)
+ {
+ best = get_better_ocsp(current, best, subject, issuer,
+ &valid, TRUE);
+ if (best && valid != VALIDATION_STALE)
+ {
+ break;
+ }
+ }
+ }
+ enumerator->destroy(enumerator);
+ }
+ DESTROY_IF(public);
+ DESTROY_IF(keyid);
+
+ /* fallback to URL fetching from subject certificate's URIs */
+ if (valid != VALIDATION_GOOD && valid != VALIDATION_REVOKED)
+ {
+ enumerator = subject->create_ocsp_uri_enumerator(subject);
+ while (enumerator->enumerate(enumerator, &uri))
+ {
+ current = fetch_ocsp(uri, &subject->interface, &issuer->interface);
+ if (current)
+ {
+ best = get_better_ocsp(current, best, subject, issuer,
+ &valid, TRUE);
+ if (best && valid != VALIDATION_STALE)
+ {
+ break;
+ }
+ }
+ }
+ enumerator->destroy(enumerator);
+ }
+ /* an uri was found, but no result. switch validation state to failed */
+ if (valid == VALIDATION_SKIPPED && uri)
+ {
+ valid = VALIDATION_FAILED;
+ }
+ if (auth)
+ {
+ auth->add(auth, AUTH_RULE_OCSP_VALIDATION, valid);
+ if (valid == VALIDATION_GOOD)
+ { /* successful OCSP check fulfills also CRL constraint */
+ auth->add(auth, AUTH_RULE_CRL_VALIDATION, VALIDATION_GOOD);
+ }
+ }
+ DESTROY_IF(best);
+ return valid;
+}
+
+/**
+ * fetch a CRL from an URL
+ */
+static certificate_t* fetch_crl(char *url)
+{
+ certificate_t *crl;
+ chunk_t chunk;
+
+ DBG1(DBG_CFG, " fetching crl from '%s' ...", url);
+ if (lib->fetcher->fetch(lib->fetcher, url, &chunk, FETCH_END) != SUCCESS)
+ {
+ DBG1(DBG_CFG, "crl fetching failed");
+ return NULL;
+ }
+ crl = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509_CRL,
+ BUILD_BLOB_ASN1_DER, chunk, BUILD_END);
+ chunk_free(&chunk);
+ if (!crl)
+ {
+ DBG1(DBG_CFG, "crl fetched successfully but parsing failed");
+ return NULL;
+ }
+ return crl;
+}
+
+/**
+ * check the signature of an CRL
+ */
+static bool verify_crl(certificate_t *crl)
+{
+ certificate_t *issuer;
+ enumerator_t *enumerator;
+ bool verified = FALSE;
+
+ enumerator = lib->credmgr->create_trusted_enumerator(lib->credmgr,
+ KEY_ANY, crl->get_issuer(crl), FALSE);
+ while (enumerator->enumerate(enumerator, &issuer, NULL))
+ {
+ if (lib->credmgr->issued_by(lib->credmgr, crl, issuer))
+ {
+ DBG1(DBG_CFG, " crl correctly signed by \"%Y\"",
+ issuer->get_subject(issuer));
+ verified = TRUE;
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+
+ return verified;
+}
+
+/**
+ * Get the better of two CRLs, and check for usable CRL info
+ */
+static certificate_t *get_better_crl(certificate_t *cand, certificate_t *best,
+ x509_t *subject, x509_t *issuer, cert_validation_t *valid, bool cache)
+{
+ enumerator_t *enumerator;
+ time_t revocation, valid_until;
+ crl_reason_t reason;
+ chunk_t serial;
+ crl_t *crl;
+
+ /* check CRL signature */
+ if (!verify_crl(cand))
+ {
+ DBG1(DBG_CFG, "crl response verification failed");
+ cand->destroy(cand);
+ return best;
+ }
+
+ crl = (crl_t*)cand;
+ enumerator = crl->create_enumerator(crl);
+ while (enumerator->enumerate(enumerator, &serial, &revocation, &reason))
+ {
+ if (chunk_equals(serial, subject->get_serial(subject)))
+ {
+ DBG1(DBG_CFG, "certificate was revoked on %T, reason: %N",
+ &revocation, TRUE, crl_reason_names, reason);
+ *valid = VALIDATION_REVOKED;
+ enumerator->destroy(enumerator);
+ DESTROY_IF(best);
+ return cand;
+ }
+ }
+ enumerator->destroy(enumerator);
+
+ /* select the better of the two CRLs */
+ if (best == NULL || crl_is_newer(crl, (crl_t*)best))
+ {
+ DESTROY_IF(best);
+ best = cand;
+ if (best->get_validity(best, NULL, NULL, &valid_until))
+ {
+ DBG1(DBG_CFG, " crl is valid: until %T", &valid_until, FALSE);
+ *valid = VALIDATION_GOOD;
+ if (cache)
+ { /* we cache non-stale crls only, as a stale crls are refetched */
+ lib->credmgr->cache_cert(lib->credmgr, best);
+ }
+ }
+ else
+ {
+ DBG1(DBG_CFG, " crl is stale: since %T", &valid_until, FALSE);
+ *valid = VALIDATION_STALE;
+ }
+ }
+ else
+ {
+ *valid = VALIDATION_STALE;
+ cand->destroy(cand);
+ }
+ return best;
+}
+
+/**
+ * validate a x509 certificate using CRL
+ */
+static cert_validation_t check_crl(x509_t *subject, x509_t *issuer,
+ auth_cfg_t *auth)
+{
+ cert_validation_t valid = VALIDATION_SKIPPED;
+ identification_t *keyid = NULL;
+ certificate_t *best = NULL;
+ certificate_t *current;
+ public_key_t *public;
+ enumerator_t *enumerator;
+ chunk_t chunk;
+ char *uri = NULL;
+
+ /* derive the authorityKeyIdentifier from the issuer's public key */
+ current = &issuer->interface;
+ public = current->get_public_key(current);
+ if (public && public->get_fingerprint(public, KEY_ID_PUBKEY_SHA1, &chunk))
+ {
+ keyid = identification_create_from_encoding(ID_KEY_ID, chunk);
+
+ /* find a cached crl by authorityKeyIdentifier */
+ enumerator = lib->credmgr->create_cert_enumerator(lib->credmgr,
+ CERT_X509_CRL, KEY_ANY, keyid, FALSE);
+ while (enumerator->enumerate(enumerator, ¤t))
+ {
+ current->get_ref(current);
+ best = get_better_crl(current, best, subject, issuer,
+ &valid, FALSE);
+ if (best && valid != VALIDATION_STALE)
+ {
+ DBG1(DBG_CFG, " using cached crl");
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+
+ /* fallback to fetching crls from credential sets cdps */
+ if (valid != VALIDATION_GOOD && valid != VALIDATION_REVOKED)
+ {
+ enumerator = lib->credmgr->create_cdp_enumerator(lib->credmgr,
+ CERT_X509_CRL, keyid);
+ while (enumerator->enumerate(enumerator, &uri))
+ {
+ current = fetch_crl(uri);
+ if (current)
+ {
+ best = get_better_crl(current, best, subject, issuer,
+ &valid, TRUE);
+ if (best && valid != VALIDATION_STALE)
+ {
+ break;
+ }
+ }
+ }
+ enumerator->destroy(enumerator);
+ }
+ keyid->destroy(keyid);
+ }
+ DESTROY_IF(public);
+
+ /* fallback to fetching crls from cdps from subject's certificate */
+ if (valid != VALIDATION_GOOD && valid != VALIDATION_REVOKED)
+ {
+ enumerator = subject->create_crl_uri_enumerator(subject);
+
+ while (enumerator->enumerate(enumerator, &uri))
+ {
+ current = fetch_crl(uri);
+ if (current)
+ {
+ best = get_better_crl(current, best, subject, issuer,
+ &valid, TRUE);
+ if (best && valid != VALIDATION_STALE)
+ {
+ break;
+ }
+ }
+ }
+ enumerator->destroy(enumerator);
+ }
+
+ /* an uri was found, but no result. switch validation state to failed */
+ if (valid == VALIDATION_SKIPPED && uri)
+ {
+ valid = VALIDATION_FAILED;
+ }
+ if (auth)
+ {
+ if (valid == VALIDATION_SKIPPED)
+ { /* if we skipped CRL validation, we use the result of OCSP for
+ * constraint checking */
+ auth->add(auth, AUTH_RULE_CRL_VALIDATION,
+ auth->get(auth, AUTH_RULE_OCSP_VALIDATION));
+ }
+ else
+ {
+ auth->add(auth, AUTH_RULE_CRL_VALIDATION, valid);
+ }
+ }
+ DESTROY_IF(best);
+ return valid;
+}
+
+METHOD(cert_validator_t, validate, bool,
+ private_revocation_validator_t *this, certificate_t *subject,
+ certificate_t *issuer, bool online, int pathlen, auth_cfg_t *auth)
+{
+ if (subject->get_type(subject) == CERT_X509 &&
+ issuer->get_type(issuer) == CERT_X509 &&
+ online)
+ {
+ DBG1(DBG_CFG, "checking certificate status of \"%Y\"",
+ subject->get_subject(subject));
+ switch (check_ocsp((x509_t*)subject, (x509_t*)issuer, auth))
+ {
+ case VALIDATION_GOOD:
+ DBG1(DBG_CFG, "certificate status is good");
+ return TRUE;
+ case VALIDATION_REVOKED:
+ /* has already been logged */
+ return FALSE;
+ case VALIDATION_SKIPPED:
+ DBG2(DBG_CFG, "ocsp check skipped, no ocsp found");
+ break;
+ case VALIDATION_STALE:
+ DBG1(DBG_CFG, "ocsp information stale, fallback to crl");
+ break;
+ case VALIDATION_FAILED:
+ DBG1(DBG_CFG, "ocsp check failed, fallback to crl");
+ break;
+ }
+ switch (check_crl((x509_t*)subject, (x509_t*)issuer, auth))
+ {
+ case VALIDATION_GOOD:
+ DBG1(DBG_CFG, "certificate status is good");
+ return TRUE;
+ case VALIDATION_REVOKED:
+ /* has already been logged */
+ return FALSE;
+ case VALIDATION_FAILED:
+ case VALIDATION_SKIPPED:
+ DBG1(DBG_CFG, "certificate status is not available");
+ break;
+ case VALIDATION_STALE:
+ DBG1(DBG_CFG, "certificate status is unknown, crl is stale");
+ break;
+ }
+ }
+ return TRUE;
+}
+
+METHOD(revocation_validator_t, destroy, void,
+ private_revocation_validator_t *this)
+{
+ free(this);
+}
+
+/**
+ * See header
+ */
+revocation_validator_t *revocation_validator_create()
+{
+ private_revocation_validator_t *this;
+
+ INIT(this,
+ .public = {
+ .validator.validate = _validate,
+ .destroy = _destroy,
+ },
+ );
+
+ return &this->public;
+}
--- /dev/null
+/*
+ * 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.
+ */
+
+/**
+ * @defgroup revocation_validator revocation_validator
+ * @{ @ingroup revocation
+ */
+
+#ifndef REVOCATION_VALIDATOR_H_
+#define REVOCATION_VALIDATOR_H_
+
+#include <credentials/cert_validator.h>
+
+typedef struct revocation_validator_t revocation_validator_t;
+
+/**
+ * Certificate validator doing CRL/OCSP checking of X509 certificates.
+ */
+struct revocation_validator_t {
+
+ /**
+ * Implements cert_validator_t interface.
+ */
+ cert_validator_t validator;
+
+ /**
+ * Destroy a revocation_validator_t.
+ */
+ void (*destroy)(revocation_validator_t *this);
+};
+
+/**
+ * Create a revocation_validator instance.
+ */
+revocation_validator_t *revocation_validator_create();
+
+#endif /** REVOCATION_VALIDATOR_H_ @}*/