From: Martin Willi Date: Wed, 29 Oct 2025 06:49:49 +0000 (+0100) Subject: revocation: Move CRL/OCSP fetch operations to a dedicated fetcher helper X-Git-Tag: 6.0.4rc1~5^2~2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=119dfc2c38a07fb73953faec73b39e80e9e8807f;p=thirdparty%2Fstrongswan.git 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. --- diff --git a/src/libstrongswan/plugins/revocation/Makefile.am b/src/libstrongswan/plugins/revocation/Makefile.am index 9532d5f033..84a879d752 100644 --- a/src/libstrongswan/plugins/revocation/Makefile.am +++ b/src/libstrongswan/plugins/revocation/Makefile.am @@ -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 diff --git a/src/libstrongswan/plugins/revocation/revocation_fetcher.c b/src/libstrongswan/plugins/revocation/revocation_fetcher.c new file mode 100644 index 0000000000..42620d35fd --- /dev/null +++ b/src/libstrongswan/plugins/revocation/revocation_fetcher.c @@ -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 . + * + * 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 +#include +#include +#include + +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; +} diff --git a/src/libstrongswan/plugins/revocation/revocation_fetcher.h b/src/libstrongswan/plugins/revocation/revocation_fetcher.h new file mode 100644 index 0000000000..4328306778 --- /dev/null +++ b/src/libstrongswan/plugins/revocation/revocation_fetcher.h @@ -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 . + * + * 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 + +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_ @}*/ diff --git a/src/libstrongswan/plugins/revocation/revocation_validator.c b/src/libstrongswan/plugins/revocation/revocation_validator.c index f78c26621f..584e6071e1 100644 --- a/src/libstrongswan/plugins/revocation/revocation_validator.c +++ b/src/libstrongswan/plugins/revocation/revocation_validator.c @@ -23,12 +23,13 @@ #include #include #include -#include #include #include #include #include +#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(), );