implement crl-persist logic
cover implement crl-persist logic
Hello OpenVPN community,
this is my first contribution (attempt) to the OpenVPN project. Please be gentle ;)
With this small patchset I am introducing a simple logic that reduces the overall client connection time when the CRL file is quite big.
Basically it consists in pre-loading the CRL file and storing it in the OpenVPN context, so that it can be later re-used for faster lookups.
The connection delay in my tests has dropped significantly (I am using a 143MB large CRL file having around 5 million entries).
Basically the delay (about 5 seconds on average) is moved from "every" client connection event to the instance bootup (or file reload).
This new behaviour can be enabled by specifying "--crl-persist" after the "--crl-file" option.
As of now, the CRL is updated only upon SIGHUP. Later this could be improved by hooking the update to another signal (i.e. SIGUSR2), or when stat() would detect a file update or even hook into inotify.
Patch 1 introduces the general logic in the openvpn code, like the new config option and the hooks to the crypto modules.
Patch 2 implements the related functions in the mbedTLS module.
Patch 3 implements the related functions in the OpenSSL module.
(Please check the specific patch commit messages for more details).
Please, have a look at my style and the way I used the various data structures. Being this my first time into the OpenVPN code base, I may have misused some objects like "c1" and similar..
Comments are welcome!
Cheers,
Antonio Quartulli (3):
openvpn: implement crl-persist logic
mbedtls: implement crl-persist related functions
openssl: implement crl-persist related functions
src/openvpn/init.c | 7 ++ src/openvpn/openvpn.h | 6 ++ src/openvpn/options.c | 21 +++++ src/openvpn/ssl_common.h | 23 ++++- src/openvpn/ssl_verify.c | 5 ++ src/openvpn/ssl_verify_backend.h | 31 +++++++ src/openvpn/ssl_verify_mbedtls.c | 102 ++++++++++++++++++---- src/openvpn/ssl_verify_openssl.c | 183 +++++++++++++++++++++++++++++++++++++++ 8 files changed, 355 insertions(+), 23 deletions(-)
Signature · 1 line
2.10.1
PATCH 1/3 openvpn: implement crl-persist logic
Right now the CRL file is parsed and loaded in memory upon every client connection. In case of large CRL files this operation introduces a non-negligible delay (order of seconds).
Some users may prefer to avoid such delay by storing the CRL file in memory and have it ready for lookup upon client connection.
Implement the logic to handle this new option (namely crl-persist) in the config parser and in the ssl_verify module.
With the current implementation, the file is reloaded only upon SIGHUP. A better logic performing an update at runtime based on the return value of stat() might be implemented later.
Note: inline or dir CRL is not supported when crl-persist is specified.
--- src/openvpn/init.c | 7 +++++++ src/openvpn/openvpn.h | 6 ++++++ src/openvpn/options.c | 21 +++++++++++++++++++++ src/openvpn/ssl_common.h | 23 +++++++++++++++++++---- src/openvpn/ssl_verify.c | 5 +++++ src/openvpn/ssl_verify_backend.h | 31 +++++++++++++++++++++++++++++++ 6 files changed, 89 insertions(+), 4 deletions(-)
src/openvpn/init.c +7 -0
@@ -2213,6 +2213,8 @@ do_init_crypto_tls_c1 (struct context *c)if (!tls_ctx_initialised (&c->c1.ks.ssl_ctx)) { +if (options->ssl_flags &SSLF_CRL_PERSIST ) +x509_load_crl_mem (&c->c1.crl, options->crl_file);/* * Initialize the OpenSSL library's global * SSL context.
@@ -2392,6 +2394,7 @@ do_init_crypto_tls (struct context *c, const unsigned int flags) to.verify_x509_name = options->verify_x509_name; to.crl_file = options->crl_file; to.crl_file_inline = options->crl_file_inline; + to.crl = &c->c1.crl; to.ssl_flags = options->ssl_flags; to.ns_cert_type = options->ns_cert_type;memmove (to.remote_cert_ku, options->remote_cert_ku,sizeof (to.remote_cert_ku));
@@ -3735,6 +3738,9 @@ close_instance (struct context *c)/* close event objects */ do_close_event_set (c); +if (c->mode ==CM_TOP ) +x509_crl_free (&c->c1.crl); +if (c->mode ==CM_P2P || c->mode ==CM_CHILD_TCP || c->mode == CM_CHILD_UDP
@@ -3827,6 +3833,7 @@ inherit_context_child (struct context *dest, dest->c1.ks.ssl_ctx = src->c1.ks.ssl_ctx; dest->c1.ks.tls_auth_key = src->c1.ks.tls_auth_key; dest->c1.ks.tls_auth_key_type = src->c1.ks.tls_auth_key_type; + dest->c1.crl = src->c1.crl;#endif /* options */
src/openvpn/openvpn.h +6 -0
@@ -199,6 +199,12 @@ struct context_1/* persist --ifconfig-pool db to file */ struct ifconfig_pool_persist *ifconfig_pool_persist;bool ifconfig_pool_persist_owned; + +#ifdef ENABLE_CRYPTO +/* persistent CRL data */ +openvpn_x509_crl_t crl; +#endif +#endif /* if client mode, hash of option strings we pulled from server */
src/openvpn/options.c +21 -0
@@ -608,6 +608,7 @@ static const char usage_message[] ="--askpass [file]: Get PEM password from controlling tty before we daemonize.\n" "--auth-nocache : Don't cache --askpass or --auth-user-pass passwords.\n" "--crl-verify crl ['dir']: Check peer certificate against a CRL.\n" +"--crl-persist : Store CRL in memory instead of reading the file every time.\n" "--tls-verify cmd: Run command cmd to verify the X509 name of a\n" " pending TLS connection that has otherwise passed all other\n" " tests of certification. cmd should return 0 to allow\n"
@@ -6974,6 +6975,26 @@ add_option (struct options *options, options->crl_file_inline = p[2 ]; } } +else if (streq (p[0 ],"crl-persist" )) + { +VERIFY_PERMISSION (OPT_P_GENERAL ); +if (!options->crl_file) + { +msg (msglevel,"--crl-persist must be specified after --crl-file" ); + goto err; + } +if (options->crl_file_inline) + { +msg (msglevel,"sorry but --crl-persist does not support inline crl file at the moment" ); + goto err; + } +if (options->ssl_flags &SSLF_CRL_VERIFY_DIR ) + { +msg (msglevel,"you cannot use --crl-persist with --crl-verify <path> dir" ); + goto err; + } + options->ssl_flags |=SSLF_CRL_PERSIST ; + }else if (streq (p[0 ],"tls-verify" ) && p[1 ]) {VERIFY_PERMISSION (OPT_P_SCRIPT );
src/openvpn/ssl_common.h +19 -4
@@ -204,6 +204,19 @@ struct key_state#endif }; +#ifdef ENABLE_CRYPTO +typedef struct +{ +#ifdef ENABLE_CRYPTO_OPENSSL +X509_NAME *issuer; +void **buff; +uint64_t num; +#elif ENABLE_CRYPTO_MBEDTLS +mbedtls_x509_crl crl; +#endif +}openvpn_x509_crl_t ; +#endif +/* * Our const options, obtained directly or derived from * command line options.
@@ -255,6 +268,7 @@ struct tls_optionsconst char *verify_x509_name;const char *crl_file;const char *crl_file_inline; +openvpn_x509_crl_t *crl;int ns_cert_type;unsigned remote_cert_ku[MAX_PARMS ];const char *remote_cert_eku;
@@ -309,10 +323,11 @@ struct tls_options# define SSLF_AUTH_USER_PASS_OPTIONAL (1<<3)# define SSLF_OPT_VERIFY (1<<4)# define SSLF_CRL_VERIFY_DIR (1<<5) -# define SSLF_TLS_VERSION_MIN_SHIFT 6 -# define SSLF_TLS_VERSION_MIN_MASK 0xF/* (uses bit positions 6 to 9) */ -# define SSLF_TLS_VERSION_MAX_SHIFT 10 -# define SSLF_TLS_VERSION_MAX_MASK 0xF/* (uses bit positions 10 to 13) */ +# define SSLF_CRL_PERSIST (1<<6) +# define SSLF_TLS_VERSION_MIN_SHIFT 7 +# define SSLF_TLS_VERSION_MIN_MASK 0xF/* (uses bit positions 7 to 10) */ +# define SSLF_TLS_VERSION_MAX_SHIFT 11 +# define SSLF_TLS_VERSION_MAX_MASK 0xF/* (uses bit positions 11 to 14) */ unsigned int ssl_flags;#ifdef MANAGEMENT_DEF_AUTH
src/openvpn/ssl_verify.c +5 -0
@@ -673,6 +673,11 @@ verify_cert(struct tls_session *session, openvpn_x509_cert_t *cert, int cert_depif (SUCCESS !=verify_check_crl_dir (opt->crl_file, cert)) goto cleanup; } +else if (opt->ssl_flags &SSLF_CRL_PERSIST ) + { +if (SUCCESS !=x509_verify_crl_mem (opt->crl, cert, subject)) + goto cleanup; + }else {if (SUCCESS !=x509_verify_crl (opt->crl_file, opt->crl_file_inline, cert, subject))
src/openvpn/ssl_verify_backend.h +31 -0
@@ -253,6 +253,37 @@ result_t x509_verify_cert_eku (openvpn_x509_cert_t *x509, const char * const expresult_t x509_write_pem (FILE *peercert_file,openvpn_x509_cert_t *peercert);/* +* Checks against an in-memory CRL object if a certificate was revoked +* +* @param crl The CRL object containing the actual CRL +* @param cert The certificate to check +* +* @return \c SUCCESS if the certificate was not found in the CRL, +* \c FAILURE if the certificate was in the list and it is +* thus revoked +*/ +result_t x509_verify_crl_mem (const openvpn_x509_crl_t *crl, +openvpn_x509_cert_t *cert,const char *subject); + +/* +* Allocates the needed resource and stores a CRL file in memory +* +* @param crl The CRL object to populate +* @param crl_file The path to the CRL file to read +* +* @return \c SUCCESS if the file is successfully loaded, +* \c FAILURE in case of any error +*/ +result_t x509_load_crl_mem (openvpn_x509_crl_t *crl,const char *crl_file); + +/* +* Release a CRL object, used to store a CRL file in memory +* +* @param crl The CRL object to free +*/ +void x509_crl_free (openvpn_x509_crl_t *crl); + +/* * Check the certificate against a CRL file. * * @param crl_file File name of the CRL file
Signature · 1 line
2.10.1
PATCH 2/3 mbedtls: implement crl-persist related functions
In reply to Antonio QuartulliImplement the functions needed by the crl-persist logic and used by the ssl_verify module.
No special data structure has been used to store the CRL as mbedtls already provides its own object and helper functions.
Tests have been performed by using a CRL file having size 143MB. Original delay upon client connection was around 4-6 seconds. With this patch the delay gets close to 0.
--- src/openvpn/ssl_verify_mbedtls.c | 102 +++++++++++++++++++++++++++++++-------- 1 file changed, 83 insertions(+), 19 deletions(-)
src/openvpn/ssl_verify_mbedtls.c +83 -19
@@ -497,6 +497,88 @@ x509_write_pem(FILE *peercert_file, mbedtls_x509_crt *peercert)return FAILURE ; } +result_t +x509_verify_crl_cert (const mbedtls_x509_crl *crl,mbedtls_x509_crt *cert, +struct gc_arena *gc,const char *crl_file, +const char *subject) +{ +result_t ret =FAILURE ; +char *serial; + +ASSERT (crl && cert); + +if ((cert->issuer_raw.len != crl->issuer_raw.len) || + (memcmp (crl->issuer_raw.p, cert->issuer_raw.p, crl->issuer_raw.len) !=0 )) + { +msg (M_WARN ,"CRL: CRL %s is from a different issuer than the issuer of " +"certificate %s" , crl_file ? crl_file :"[in-memory]" , subject); + ret =SUCCESS ; + goto end; + } + +if (!mbed_ok (mbedtls_x509_crt_is_revoked (cert, crl))) + { + serial =backend_x509_get_serial_hex (cert, gc); +msg (D_HANDSHAKE ,"CRL CHECK FAILED: %s (serial %s) is REVOKED" , subject, + (serial ? serial :"NOT AVAILABLE" )); + goto end; + } + + ret =SUCCESS ; +msg (D_HANDSHAKE ,"CRL CHECK OK: %s" , subject); +end: + +return ret; +} + +result_t +x509_verify_crl_mem (const openvpn_x509_crl_t *crl,mbedtls_x509_crt *cert, +const char *subject) +{ +struct gc_arena gc =gc_new (); +int ret; + +ASSERT (crl && cert); + + ret =x509_verify_crl_cert (&crl->crl, cert, &gc,NULL , subject); + +gc_free (&gc); + +return ret; +} + +void +x509_crl_free (openvpn_x509_crl_t *crl) +{ +ASSERT (crl); + +mbedtls_x509_crl_free (&crl->crl); +memset (&crl->crl,0 ,sizeof (crl->crl)); +} + +result_t +x509_load_crl_mem (openvpn_x509_crl_t *crl,const char *crl_file) +{ +result_t ret =FAILURE ; + +ASSERT (crl && crl_file); + +x509_crl_free (crl); + +msg (D_TLS_DEBUG_LOW ,"CRL-persist: loading file: %s" , crl_file); + +if (!mbed_ok (mbedtls_x509_crl_parse_file (&crl->crl, crl_file))) + { +msg (M_WARN ,"CRL: cannot read: %s" , crl_file); + goto end; + } + + ret =SUCCESS ; +end: + +return ret; +} +/* * check peer cert against CRL */
@@ -507,7 +589,6 @@ x509_verify_crl(const char *crl_file, const char *crl_inline,result_t retval =FAILURE ;mbedtls_x509_crl crl = {0 };struct gc_arena gc =gc_new (); -char *serial;if (!strcmp (crl_file,INLINE_FILE_TAG ) && crl_inline) {
@@ -527,24 +608,7 @@ x509_verify_crl(const char *crl_file, const char *crl_inline, } } -if (cert->issuer_raw.len != crl.issuer_raw.len || -memcmp (crl.issuer_raw.p, cert->issuer_raw.p, crl.issuer_raw.len) !=0 ) - { -msg (M_WARN ,"CRL: CRL %s is from a different issuer than the issuer of " -"certificate %s" , crl_file, subject); - retval =SUCCESS ; - goto end; - } - -if (!mbed_ok (mbedtls_x509_crt_is_revoked (cert, &crl))) - { - serial =backend_x509_get_serial_hex (cert, &gc); -msg (D_HANDSHAKE ,"CRL CHECK FAILED: %s (serial %s) is REVOKED" , subject, (serial ? serial :"NOT AVAILABLE" )); - goto end; - } - - retval =SUCCESS ; -msg (D_HANDSHAKE ,"CRL CHECK OK: %s" ,subject); + retval =x509_verify_crl_cert (&crl, cert, &gc, crl_file, subject); end:gc_free (&gc);
Signature · 1 line
2.10.1
PATCH 3/3 openssl: implement crl-persist related functions
In reply to Antonio QuartulliImplement the functions needed by the crl-persist logic when openssl is enabled. Such functions are used in the ssl_verify module.
Note that the CRL file is stored in an adhoc data structure and no openssl specific object is used. The data structure being used is a sorted array or serials that can later be looked up in log(N) with a binary search, thus guaranteeing a fast lookup operation.
Such data structure may be changed in the future with an optimized openssl specific object.
Tests have been performed by using a CRL file having size 143MB. Original delay upon client connection was around 5-8 seconds. With this patch the delay gets close to 0.
--- src/openvpn/ssl_verify_openssl.c | 183 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 183 insertions(+)
src/openvpn/ssl_verify_openssl.c +183 -0
@@ -47,6 +47,9 @@#include <openssl/x509v3.h> #include <openssl/err.h> +#define __STDC_FORMAT_MACROS +#include <inttypes.h> +int verify_callback (int preverify_ok,X509_STORE_CTX * ctx) {
@@ -625,6 +628,186 @@ x509_write_pem(FILE *peercert_file, X509 *peercert)return SUCCESS ; } +static int +x509_serial_cmp (const void *a,const void *b) +{ +const ASN1_INTEGER **serial1 = (const ASN1_INTEGER **)a; +const ASN1_INTEGER **serial2 = (const ASN1_INTEGER **)b; + +ASSERT (serial1 && *serial1 && serial2 && *serial2); + +return ASN1_INTEGER_cmp (*serial1, *serial2); +} + +result_t +x509_verify_crl_mem (const openvpn_x509_crl_t *crl,X509 *cert, +const char *subject) +{ +result_t ret =FAILURE ; +struct gc_arena gc =gc_new (); +ASN1_INTEGER *serial; + +ASSERT (crl && cert); + +/* authorize the client if the CRL list is empty */ +if (crl->num ==0 ) + { + ret =SUCCESS ; + goto end; + } + +/* +* make sure the issuer of the CRL and the certificate are the same, +* otherwise the check cannot be performed +*/ +if (X509_NAME_cmp (crl->issuer,X509_get_issuer_name (cert)) !=0 ) + { +msg (M_WARN ,"CRL: CRL [in-memory] is from a different issuer than the issuer of certificate %s" , + subject); + ret =SUCCESS ; + goto end; + } + + serial =X509_get_serialNumber (cert); +if (!serial) + { +msg (M_WARN ,"CRL: can't extract serial from cert" ); + goto end; + } + +/* +* perform binary search on sorted CRL array. The cmp function assumes that +* the arguments are all pointers to pointers, therefore the address of the +* "key" object has to be passed instead of the object itself. +*/ +if (bsearch (&serial, crl->buff, crl->num,sizeof (ASN1_INTEGER *), + x509_serial_cmp)) + { +fprintf (stderr,"!\n" ); +msg (D_HANDSHAKE ,"CRL CHECK FAILED: %s (serial %s) is REVOKED" , subject, +format_hex_ex (serial->data, serial->length,0 ,1 ,":" , &gc)); + goto end; + } + +/* serial not found in CRL: certificate is valid */ + ret =SUCCESS ; +msg (D_HANDSHAKE ,"CRL CHECK OK: %s" , subject); + +end: +gc_free (&gc); + +return ret; +} + +void +x509_crl_free (openvpn_x509_crl_t *crl) +{ +int i; + +ASSERT (crl); + +for (i =0 ; i < crl->num; i++) +ASN1_INTEGER_free (crl->buff[i]); + +free (crl->buff); +X509_NAME_free (crl->issuer); + crl->buff =NULL ; + crl->issuer =NULL ; + crl->num =0 ; +} + +result_t +x509_load_crl_mem (openvpn_x509_crl_t *crl,const char *crl_file) +{ +result_t ret =FAILURE ; +X509_CRL *x509_crl =NULL ; +X509_REVOKED *revoked; +X509_NAME *issuer; +BIO *in =NULL ; +uint64_t num, i; + +ASSERT (crl && crl_file); + +msg (D_TLS_DEBUG_LOW ,"CRL-persist: loading file: %s" , crl_file); + +/* free previously allocated data - useful in case or context update */ +if (crl->buff) +x509_crl_free (crl); + + in =BIO_new_file (crl_file,"r" ); +if (!in) + { +msg (M_WARN ,"CRL: cannot read: %s" , crl_file); + goto end; + } + + x509_crl =PEM_read_bio_X509_CRL (in,NULL ,NULL ,NULL ); +if (!x509_crl) + { +msg (M_WARN ,"CRL: cannot read CRL from file %s" , crl_file); + goto end; + } + + crl->num =0 ; + crl->buff =NULL ; + crl->issuer =NULL ; + + issuer =X509_CRL_get_issuer (x509_crl); +if (issuer) + crl->issuer =X509_NAME_dup (issuer); + + num =sk_X509_REVOKED_num (X509_CRL_get_REVOKED (x509_crl)); +/* CRL is empty */ +if (num == (uint64_t )-1 ) + { + ret =SUCCESS ; + goto end; + } + + crl->num = num; + crl->buff =malloc (sizeof (ASN1_INTEGER **) * num); +if (!crl->buff) + { +msg (M_WARN ,"CRL: cannot allocate CRL buffer for file %s" , crl_file); + goto end; + } + +/* store each serial number in the buffer array as ASN1_INTEGER objects */ +for (i =0 ; i < num; i++) + { + revoked =sk_X509_REVOKED_value (X509_CRL_get_REVOKED (x509_crl), i); + crl->buff[i] =ASN1_INTEGER_dup (revoked->serialNumber); +if (!crl->buff[i]) + { +msg (M_WARN ,"CRL: cannot store CRL serial for file %s" , crl_file); + goto err; + } + } + +/* sort the array for fast lookup later */ +qsort (crl->buff, crl->num,sizeof (ASN1_INTEGER *), x509_serial_cmp); +msg (D_TLS_DEBUG_LOW ,"CRL-persist: loaded and sorted %" PRIu64" serials" , + crl->num); + + ret =SUCCESS ; + goto end; +err: +/* rollback the for-loop and release all the allocated memory */ +for (;i >0 ; i--) +ASN1_INTEGER_free (crl->buff[i -1 ]); + +free (crl->buff); +X509_NAME_free (crl->issuer); + +end: +if (in) +BIO_free (in); +if (x509_crl) +X509_CRL_free (x509_crl); + +return ret; +} +/* * check peer cert against CRL */
Signature · 1 line
2.10.1
Re: [Openvpn-devel] [PATCH 3/3] openssl: implement crl-persist related functions
Quoted 8 lines · Antonio Quartulli
Implement the functions needed by the crl-persist logic when openssl is enabled. Such functions are used in the
ssl_verifymodule.Note that the CRL file is stored in an adhoc data structure and no openssl specific object is used. The data structure being used is a sorted array or serials that can later be looked up in log(N) with a binary search, thus guaranteeing a fast lookup operation.
Quoted 8 lines
Such data structure may be changed in the future with an optimized openssl specific object.
Tests have been performed by using a CRL file having size 143MB. Original delay upon client connection was around 5-8 seconds. With this patch the delay gets close to 0.
Signed-off-by Antonio Quartulli <[email protected]...>
As discussed on IRC, it might be better to first change the CRL handling code in the OpenSSL module to use the internal routines provided by the OpenSSL library. (apparently a patch to implement this change is in the work on somebody else's desk)
At that point my patch could be changed to re-use the same code instead of implementing my own optimized logic.
Note: also OpenSSL uses a sorted array + bsearch for CRL handling, therefore the performance of OpenSSL vs my approach should be similar.
Does anybody else have any opinion against this?
Cheers,
Signature · 1 line
Antonio Quartulli
Re: [Openvpn-devel] [PATCH 3/3] openssl: implement crl-persist related functions
Quoted 28 lines · Antonio Quartulli
Quoted 8 lines · Antonio Quartulli
Implement the functions needed by the crl-persist logic when openssl is enabled. Such functions are used in the
ssl_verifymodule.Note that the CRL file is stored in an adhoc data structure and no openssl specific object is used. The data structure being used is a sorted array or serials that can later be looked up in log(N) with a binary search, thus guaranteeing a fast lookup operation.
Quoted 8 lines
Such data structure may be changed in the future with an optimized openssl specific object.
Tests have been performed by using a CRL file having size 143MB. Original delay upon client connection was around 5-8 seconds. With this patch the delay gets close to 0.
Signed-off-by Antonio Quartulli <[email protected]...>As discussed on IRC, it might be better to first change the CRL handling code in the OpenSSL module to use the internal routines provided by the OpenSSL library. (apparently a patch to implement this change is in the work on somebody else's desk)
At that point my patch could be changed to re-use the same code instead of implementing my own optimized logic.
Quoted 5 lines
Note: also OpenSSL uses a sorted array + bsearch for CRL handling, therefore the performance of OpenSSL vs my approach should be similar.
Does anybody else have any opinion against this?
First of all, thank you very much for this patch set! As I said on the IRC, this patch-set is well done when it comes to overall style and
layout. I have not dug into the details, but the overall quick look
gave a good impression.
This new feature sounds reasonable. The improved OpenSSL CRL handling will be most welcome. So basing your work on top of that makes a lot of sense, and hopefully it can simplify your patch as well.
But I also think that we should look more carefully at the whole CRL handling as a hole unit. To me the --crl-persist looks like a reasonable feature, but I'm still hoping to see more responses to this feature from others as well. But if it turns out that we can implement this feature and also improve the current CRL handling, that will hopefully give even less code duplication and an even simpler patch to when adding the --crl-persist feature.
On IRC we also briefly discussed that the mbedTLS CRL code oaths is not too clever and efficient (like, no btree search, just a simple for-loop). I would encourage you to look if you can improve the mbedTLS code too, and provide that patch to them. I'm sure the mbedTLS guys will appreciate that effort. This will again indirectly benefit us, and we need to have even less specialized code to make the mbedTLS integration perform well. So it's a win-win for all of us :)
And I'm happy to see more people looking into these parts of the OpenVPN code as well! So thank you!
Signature · 3 lines
kind regards, David Sommerseth
Quoted 2 lines · Antonio Quartulli
Hello OpenVPN community,
not sure if this is any helpful, but I have also prepared a branch with these changes on github at https://github.com/ordex/openvpn/tree/crl-persist
Maybe somebody prefers a comfy webbrowser when reviewing patches :)
Cheers,
Signature · 1 line
Antonio Quartulli