haproxy/include/haproxy/ssl_ocsp-t.h
Remi Tricot-Le Breton 69071490ff BUG/MAJOR: ocsp: Separate refcount per instance and per store
With the current way OCSP responses are stored, a single OCSP response
is stored (in a certificate_ocsp structure) when it is loaded during a
certificate parsing, and each SSL_CTX that references it increments its
refcount. The reference to the certificate_ocsp is kept in the SSL_CTX
linked to each ckch_inst, in an ex_data entry that gets freed when the
context is freed.
One of the downsides of this implementation is that if every ckch_inst
referencing a certificate_ocsp gets detroyed, then the OCSP response is
removed from the system. So if we were to remove all crt-list lines
containing a given certificate (that has an OCSP response), and if all
the corresponding SSL_CTXs were destroyed (no ongoing connection using
them), the OCSP response would be destroyed even if the certificate
remains in the system (as an unused certificate).
In such a case, we would want the OCSP response not to be "usable",
since it is not used by any ckch_inst, but still remain in the OCSP
response tree so that if the certificate gets reused (via an "add ssl
crt-list" command for instance), its OCSP response is still known as
well.
But we would also like such an entry not to be updated automatically
anymore once no instance uses it. An easy way to do it could have been
to keep a reference to the certificate_ocsp structure in the ckch_store
as well, on top of all the ones in the ckch_instances, and to remove the
ocsp response from the update tree once the refcount falls to 1, but it
would not work because of the way the ocsp response tree keys are
calculated. They are decorrelated from the ckch_store and are the actual
OCSP_CERTIDs, which is a combination of the issuer's name hash and key
hash, and the certificate's serial number. So two copies of the same
certificate but with different names would still point to the same ocsp
response tree entry.

The solution that answers to all the needs expressed aboved is actually
to have two reference counters in the certificate_ocsp structure, one
actual reference counter corresponding to the number of "live" pointers
on the certificate_ocsp structure, incremented for every SSL_CTX using
it, and one for the ckch stores.
If the ckch_store reference counter falls to 0, the corresponding
certificate must have been removed via CLI calls ('set ssl cert' for
instance).
If the actual refcount falls to 0, then no live SSL_CTX uses the
response anymore. It could happen if all the corresponding crt-list
lines were removed and there are no live SSL sessions using the
certificate anymore.
If any of the two refcounts becomes 0, we will always remove the
response from the auto update tree, because there's no point in spending
time updating an OCSP response that no new SSL connection will be able
to use. But the certificate_ocsp object won't be removed from the tree
unless both refcounts are 0.

Must be backported up to 2.8. Wait a little bit before backporting.
2024-03-20 16:12:10 +01:00

94 lines
3.1 KiB
C

/*
* include/haproxy/ssl_ocsp-t.h
* SSL structures related to OCSP
*
* Copyright (C) 2022 Remi Tricot-Le Breton - rlebreton@haproxy.com
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation, version 2.1
* exclusively.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _HAPROXY_SSL_OCSP_T_H
#define _HAPROXY_SSL_OCSP_T_H
#ifdef USE_OPENSSL
#include <import/ebtree-t.h>
#include <haproxy/buf-t.h>
#include <haproxy/openssl-compat.h>
#include <haproxy/ssl_sock-t.h>
#ifndef OPENSSL_NO_OCSP
extern int ocsp_ex_index;
#endif
#define SSL_OCSP_UPDATE_DELAY_MAX 60*60 /* 1H */
#define SSL_OCSP_UPDATE_DELAY_MIN 5*60 /* 5 minutes */
#define SSL_OCSP_UPDATE_MARGIN 60 /* 1 minute */
#define SSL_OCSP_HTTP_ERR_REPLAY 60 /* 1 minute */
#if (defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB && !defined OPENSSL_NO_OCSP)
/*
* struct alignment works here such that the key.key is the same as key_data
* Do not change the placement of key_data
*/
struct certificate_ocsp {
struct ebmb_node key;
unsigned char key_data[OCSP_MAX_CERTID_ASN1_LENGTH];
unsigned int key_length;
int refcount_store; /* Number of ckch_store that reference this certificate_ocsp */
int refcount; /* Number of actual references to this certificate_ocsp (SSL_CTXs mostly) */
struct buffer response;
long expire;
X509 *issuer;
STACK_OF(X509) *chain;
struct eb64_node next_update; /* Key of items inserted in ocsp_update_tree (sorted by absolute date) */
struct buffer *uri; /* First OCSP URI contained in the corresponding certificate */
/* OCSP update stats */
u64 last_update; /* Time of last successful update */
unsigned int last_update_status;/* Status of the last OCSP update */
unsigned int num_success; /* Number of successful updates */
unsigned int num_failure; /* Number of failed updates */
unsigned int fail_count:31; /* Number of successive failures */
unsigned int update_once:1; /* Set if an entry should not be reinserted into te tree after update */
char path[VAR_ARRAY];
};
struct ocsp_cbk_arg {
int is_single;
int single_kt;
union {
struct certificate_ocsp *s_ocsp;
/*
* m_ocsp will have multiple entries dependent on key type
* Entry 0 - DSA
* Entry 1 - ECDSA
* Entry 2 - RSA
*/
struct certificate_ocsp *m_ocsp[SSL_SOCK_NUM_KEYTYPES];
};
};
extern struct eb_root cert_ocsp_tree;
extern struct eb_root ocsp_update_tree;
extern struct task *ocsp_update_task;
__decl_thread(extern HA_SPINLOCK_T ocsp_tree_lock);
#endif /* (defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB && !defined OPENSSL_NO_OCSP) */
#endif /* USE_OPENSSL */
#endif /* _HAPROXY_SSL_OCSP_T_H */