revocation: Move CRL/OCSP fetch operations to a dedicated fetcher helper
Before adding stateful CRL fetching extensions, refactor CRL fetching to a helper class for better separation. While there are currently no plans to extend OCSP fetching, move it as well for consistency.
This commit is contained in:
committed by
Tobias Brunner
parent
f739f7a075
commit
119dfc2c38
@@ -12,6 +12,7 @@ endif
|
||||
|
||||
libstrongswan_revocation_la_SOURCES = \
|
||||
revocation_plugin.h revocation_plugin.c \
|
||||
revocation_fetcher.h revocation_fetcher.c \
|
||||
revocation_validator.h revocation_validator.c
|
||||
|
||||
libstrongswan_revocation_la_LDFLAGS = -module -avoid-version
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* Copyright (C) 2025 Martin Willi
|
||||
* Copyright (C) 2015-2018 Tobias Brunner
|
||||
* Copyright (C) 2009-2022 Andreas Steffen
|
||||
*
|
||||
* Copyright (C) secunet Security Networks 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_fetcher.h"
|
||||
|
||||
#include <utils/debug.h>
|
||||
#include <credentials/certificates/crl.h>
|
||||
#include <credentials/certificates/ocsp_request.h>
|
||||
#include <credentials/certificates/ocsp_response.h>
|
||||
|
||||
typedef struct private_revocation_fetcher_t private_revocation_fetcher_t;
|
||||
|
||||
/**
|
||||
* Private data of an revocation_fetcher_t object.
|
||||
*/
|
||||
struct private_revocation_fetcher_t {
|
||||
|
||||
/**
|
||||
* Public revocation_fetcher_t interface.
|
||||
*/
|
||||
revocation_fetcher_t public;
|
||||
};
|
||||
|
||||
METHOD(revocation_fetcher_t, fetch_crl, certificate_t *,
|
||||
private_revocation_fetcher_t *this, char *url, u_int timeout)
|
||||
{
|
||||
certificate_t *crl;
|
||||
chunk_t chunk = chunk_empty;
|
||||
|
||||
DBG1(DBG_CFG, " fetching crl from '%s' ...", url);
|
||||
if (lib->fetcher->fetch(lib->fetcher, url, &chunk,
|
||||
FETCH_TIMEOUT, timeout,
|
||||
FETCH_END) != SUCCESS)
|
||||
{
|
||||
DBG1(DBG_CFG, "crl fetching failed");
|
||||
chunk_free(&chunk);
|
||||
return NULL;
|
||||
}
|
||||
crl = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509_CRL,
|
||||
BUILD_BLOB_PEM, chunk, BUILD_END);
|
||||
chunk_free(&chunk);
|
||||
if (!crl)
|
||||
{
|
||||
DBG1(DBG_CFG, "crl fetched successfully but parsing failed");
|
||||
return NULL;
|
||||
}
|
||||
return crl;
|
||||
}
|
||||
|
||||
METHOD(revocation_fetcher_t, fetch_ocsp, certificate_t*,
|
||||
private_revocation_fetcher_t *this, char *url,
|
||||
certificate_t *subject, certificate_t *issuer, u_int timeout)
|
||||
{
|
||||
certificate_t *request, *response;
|
||||
ocsp_request_t *ocsp_request;
|
||||
ocsp_response_t *ocsp_response;
|
||||
chunk_t send, receive = chunk_empty;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
if (!request->get_encoding(request, CERT_ASN1_DER, &send))
|
||||
{
|
||||
DBG1(DBG_CFG, "encoding ocsp request failed");
|
||||
request->destroy(request);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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_TIMEOUT, timeout,
|
||||
FETCH_END) != SUCCESS)
|
||||
{
|
||||
DBG1(DBG_CFG, "ocsp request to %s failed", url);
|
||||
request->destroy(request);
|
||||
chunk_free(&receive);
|
||||
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");
|
||||
request->destroy(request);
|
||||
return NULL;
|
||||
}
|
||||
ocsp_response = (ocsp_response_t*)response;
|
||||
if (ocsp_response->get_ocsp_status(ocsp_response) != OCSP_SUCCESSFUL)
|
||||
{
|
||||
response->destroy(response);
|
||||
request->destroy(request);
|
||||
return NULL;
|
||||
}
|
||||
ocsp_request = (ocsp_request_t*)request;
|
||||
if (ocsp_response->get_nonce(ocsp_response).len &&
|
||||
!chunk_equals_const(ocsp_request->get_nonce(ocsp_request),
|
||||
ocsp_response->get_nonce(ocsp_response)))
|
||||
{
|
||||
DBG1(DBG_CFG, "nonce in ocsp response doesn't match");
|
||||
request->destroy(request);
|
||||
return NULL;
|
||||
}
|
||||
request->destroy(request);
|
||||
return response;
|
||||
}
|
||||
|
||||
METHOD(revocation_fetcher_t, destroy, void,
|
||||
private_revocation_fetcher_t *this)
|
||||
{
|
||||
free(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* See header
|
||||
*/
|
||||
revocation_fetcher_t *revocation_fetcher_create()
|
||||
{
|
||||
private_revocation_fetcher_t *this;
|
||||
|
||||
INIT(this,
|
||||
.public = {
|
||||
.fetch_crl = _fetch_crl,
|
||||
.fetch_ocsp = _fetch_ocsp,
|
||||
.destroy = _destroy,
|
||||
},
|
||||
);
|
||||
|
||||
return &this->public;
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (C) 2025 Martin Willi
|
||||
*
|
||||
* Copyright (C) secunet Security Networks 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_fetcher revocation_fetcher
|
||||
* @{ @ingroup revocation
|
||||
*/
|
||||
|
||||
#ifndef REVOCATION_FETCHER_H_
|
||||
#define REVOCATION_FETCHER_H_
|
||||
|
||||
#include <credentials/certificates/certificate.h>
|
||||
|
||||
typedef struct revocation_fetcher_t revocation_fetcher_t;
|
||||
|
||||
/**
|
||||
* Certificate fetcher performing the CRL/OCSP transfer.
|
||||
*/
|
||||
struct revocation_fetcher_t {
|
||||
|
||||
/**
|
||||
* Fetch a CRL from given URL.
|
||||
*
|
||||
* @param this revocation fetcher
|
||||
* @param url URL to retrieve the CRL from
|
||||
* @param timeout timeout in seconds for the fetch operation
|
||||
* @return fetched CRL or NULL on error
|
||||
*/
|
||||
certificate_t *(*fetch_crl)(revocation_fetcher_t *this, char *url,
|
||||
u_int timeout);
|
||||
|
||||
/**
|
||||
* Fetch an OCSP response from given URL.
|
||||
*
|
||||
* @param this revocation fetcher
|
||||
* @param url URL to retrieve the OCSP response from
|
||||
* @param subject subject to request OSCP status for
|
||||
* @param issuer issuer of the subject
|
||||
* @param timeout timeout in seconds for the fetch operation
|
||||
* @return fetched OCSP response or NULL on error
|
||||
*/
|
||||
certificate_t *(*fetch_ocsp)(revocation_fetcher_t *this, char *url,
|
||||
certificate_t *subject, certificate_t *issuer,
|
||||
u_int timeout);
|
||||
|
||||
/**
|
||||
* Destroy a revocation_fetcher_t.
|
||||
*/
|
||||
void (*destroy)(revocation_fetcher_t *this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a revocation_fetcher instance.
|
||||
*/
|
||||
revocation_fetcher_t *revocation_fetcher_create();
|
||||
|
||||
#endif /** REVOCATION_FETCHER_H_ @}*/
|
||||
@@ -23,12 +23,13 @@
|
||||
#include <utils/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>
|
||||
#include <threading/spinlock.h>
|
||||
|
||||
#include "revocation_fetcher.h"
|
||||
|
||||
/**
|
||||
* Default timeout in seconds when fetching OCSP/CRL.
|
||||
*/
|
||||
@@ -46,6 +47,11 @@ struct private_revocation_validator_t {
|
||||
*/
|
||||
revocation_validator_t public;
|
||||
|
||||
/**
|
||||
* Fetch helper for CRL/OCSP.
|
||||
*/
|
||||
revocation_fetcher_t *fetcher;
|
||||
|
||||
/**
|
||||
* Enable OCSP validation
|
||||
*/
|
||||
@@ -67,80 +73,6 @@ struct private_revocation_validator_t {
|
||||
spinlock_t *lock;
|
||||
};
|
||||
|
||||
/**
|
||||
* Do an OCSP request
|
||||
*/
|
||||
static certificate_t *fetch_ocsp(char *url, certificate_t *subject,
|
||||
certificate_t *issuer, u_int timeout)
|
||||
{
|
||||
certificate_t *request, *response;
|
||||
ocsp_request_t *ocsp_request;
|
||||
ocsp_response_t *ocsp_response;
|
||||
chunk_t send, receive = chunk_empty;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
if (!request->get_encoding(request, CERT_ASN1_DER, &send))
|
||||
{
|
||||
DBG1(DBG_CFG, "encoding ocsp request failed");
|
||||
request->destroy(request);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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_TIMEOUT, timeout,
|
||||
FETCH_END) != SUCCESS)
|
||||
{
|
||||
DBG1(DBG_CFG, "ocsp request to %s failed", url);
|
||||
request->destroy(request);
|
||||
chunk_free(&receive);
|
||||
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");
|
||||
request->destroy(request);
|
||||
return NULL;
|
||||
}
|
||||
ocsp_response = (ocsp_response_t*)response;
|
||||
if (ocsp_response->get_ocsp_status(ocsp_response) != OCSP_SUCCESSFUL)
|
||||
{
|
||||
response->destroy(response);
|
||||
request->destroy(request);
|
||||
return NULL;
|
||||
}
|
||||
ocsp_request = (ocsp_request_t*)request;
|
||||
if (ocsp_response->get_nonce(ocsp_response).len &&
|
||||
!chunk_equals_const(ocsp_request->get_nonce(ocsp_request),
|
||||
ocsp_response->get_nonce(ocsp_response)))
|
||||
{
|
||||
DBG1(DBG_CFG, "nonce in ocsp response doesn't match");
|
||||
request->destroy(request);
|
||||
return NULL;
|
||||
}
|
||||
request->destroy(request);
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify OCSP response signature
|
||||
*/
|
||||
@@ -369,8 +301,9 @@ static cert_validation_t check_ocsp(private_revocation_validator_t *this,
|
||||
CERT_X509_OCSP_RESPONSE, keyid);
|
||||
while (enumerator->enumerate(enumerator, &uri))
|
||||
{
|
||||
current = fetch_ocsp(uri, &subject->interface, &issuer->interface,
|
||||
timeout);
|
||||
current = this->fetcher->fetch_ocsp(this->fetcher, uri,
|
||||
&subject->interface,
|
||||
&issuer->interface, timeout);
|
||||
if (current)
|
||||
{
|
||||
best = get_better_ocsp(current, best, subject, issuer,
|
||||
@@ -392,8 +325,9 @@ static cert_validation_t check_ocsp(private_revocation_validator_t *this,
|
||||
enumerator = subject->create_ocsp_uri_enumerator(subject);
|
||||
while (enumerator->enumerate(enumerator, &uri))
|
||||
{
|
||||
current = fetch_ocsp(uri, &subject->interface, &issuer->interface,
|
||||
timeout);
|
||||
current = this->fetcher->fetch_ocsp(this->fetcher, uri,
|
||||
&subject->interface,
|
||||
&issuer->interface, timeout);
|
||||
if (current)
|
||||
{
|
||||
best = get_better_ocsp(current, best, subject, issuer,
|
||||
@@ -428,34 +362,6 @@ static cert_validation_t check_ocsp(private_revocation_validator_t *this,
|
||||
return valid;
|
||||
}
|
||||
|
||||
/**
|
||||
* fetch a CRL from an URL
|
||||
*/
|
||||
static certificate_t* fetch_crl(char *url, u_int timeout)
|
||||
{
|
||||
certificate_t *crl;
|
||||
chunk_t chunk = chunk_empty;
|
||||
|
||||
DBG1(DBG_CFG, " fetching crl from '%s' ...", url);
|
||||
if (lib->fetcher->fetch(lib->fetcher, url, &chunk,
|
||||
FETCH_TIMEOUT, timeout,
|
||||
FETCH_END) != SUCCESS)
|
||||
{
|
||||
DBG1(DBG_CFG, "crl fetching failed");
|
||||
chunk_free(&chunk);
|
||||
return NULL;
|
||||
}
|
||||
crl = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509_CRL,
|
||||
BUILD_BLOB_PEM, 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
|
||||
*/
|
||||
@@ -649,7 +555,7 @@ static cert_validation_t find_crl(private_revocation_validator_t *this,
|
||||
while (enumerator->enumerate(enumerator, &uri))
|
||||
{
|
||||
*uri_found = TRUE;
|
||||
current = fetch_crl(uri, timeout);
|
||||
current = this->fetcher->fetch_crl(this->fetcher, uri, timeout);
|
||||
if (current)
|
||||
{
|
||||
if (!current->has_issuer(current, issuer))
|
||||
@@ -741,7 +647,7 @@ static cert_validation_t check_delta_crl(private_revocation_validator_t *this,
|
||||
while (valid != VALIDATION_GOOD && valid != VALIDATION_REVOKED &&
|
||||
enumerator->enumerate(enumerator, &cdp))
|
||||
{
|
||||
current = fetch_crl(cdp->uri, timeout);
|
||||
current = this->fetcher->fetch_crl(this->fetcher, cdp->uri, timeout);
|
||||
if (current)
|
||||
{
|
||||
if (!check_issuer(current, issuer, cdp))
|
||||
@@ -815,7 +721,8 @@ static cert_validation_t check_crl(private_revocation_validator_t *this,
|
||||
while (enumerator->enumerate(enumerator, &cdp))
|
||||
{
|
||||
uri_found = TRUE;
|
||||
current = fetch_crl(cdp->uri, timeout);
|
||||
current = this->fetcher->fetch_crl(this->fetcher, cdp->uri,
|
||||
timeout);
|
||||
if (current)
|
||||
{
|
||||
if (!check_issuer(current, issuer, cdp))
|
||||
@@ -1023,6 +930,7 @@ METHOD(revocation_validator_t, reload, void,
|
||||
METHOD(revocation_validator_t, destroy, void,
|
||||
private_revocation_validator_t *this)
|
||||
{
|
||||
this->fetcher->destroy(this->fetcher);
|
||||
this->lock->destroy(this->lock);
|
||||
free(this);
|
||||
}
|
||||
@@ -1041,6 +949,7 @@ revocation_validator_t *revocation_validator_create()
|
||||
.reload = _reload,
|
||||
.destroy = _destroy,
|
||||
},
|
||||
.fetcher = revocation_fetcher_create(),
|
||||
.lock = spinlock_create(),
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user