mirror of
https://github.com/OpenVPN/openvpn.git
synced 2024-09-19 19:42:30 +02:00
Implement tls-groups option to specify eliptic curves/groups
By default OpenSSL 1.1+ only allows signatures and ecdh/ecdhx from the default list of X25519:secp256r1:X448:secp521r1:secp384r1. In TLS1.3 key exchange is independent from the signature/key of the certificates, so allowing all groups per default is not a sensible choice anymore and instead a shorter list is reasonable. However, when using certificates with exotic curves that are not on the group list, the signatures of these certificates will no longer be accepted. The tls-groups option allows to modify the group list to account for these corner cases. Patch V2: Uses local gc_arena instead of malloc/free, reword commit message. Fix other typos/clarify messages Patch V3: Style fixes, adjust code to changes from mbedTLS session fix Patch V5: Fix compilation with OpenSSL 1.0.2 Patch V6: Redo the 'while((token = strsep(&tmp_groups, ":"))' change which accidentally got lost. Signed-off-by: Arne Schwabe <arne@rfc2549.org> Acked-by: Antonio Quartulli <antonio@openvpn.net> Message-Id: <20200721154922.17144-1-arne@rfc2549.org> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg20521.html Signed-off-by: Gert Doering <gert@greenie.muc.de>
This commit is contained in:
parent
ba66faad56
commit
8353ae8075
@ -12,14 +12,15 @@ OpenVPN 2.4.0 and newer automatically initialize ECDH parameters. When ECDSA is
|
|||||||
used for authentication, the curve used for the server certificate will be used
|
used for authentication, the curve used for the server certificate will be used
|
||||||
for ECDH too. When autodetection fails (e.g. when using RSA certificates)
|
for ECDH too. When autodetection fails (e.g. when using RSA certificates)
|
||||||
OpenVPN lets the crypto library decide if possible, or falls back to the
|
OpenVPN lets the crypto library decide if possible, or falls back to the
|
||||||
secp384r1 curve.
|
secp384r1 curve. The list of groups/curves that the crypto library will choose
|
||||||
|
from can be set with the --tls-groups <grouplist> option.
|
||||||
|
|
||||||
An administrator can force an OpenVPN/OpenSSL server to use a specific curve
|
An administrator can force an OpenVPN/OpenSSL server to use a specific curve
|
||||||
using the --ecdh-curve <curvename> option with one of the curves listed as
|
using the --ecdh-curve <curvename> option with one of the curves listed as
|
||||||
available by the --show-curves option. Clients will use the same curve as
|
available by the --show-groups option. Clients will use the same curve as
|
||||||
selected by the server.
|
selected by the server.
|
||||||
|
|
||||||
Note that not all curves listed by --show-curves are available for use with TLS;
|
Note that not all curves listed by --show-groups are available for use with TLS;
|
||||||
in that case connecting will fail with a 'no shared cipher' TLS error.
|
in that case connecting will fail with a 'no shared cipher' TLS error.
|
||||||
|
|
||||||
Authentication (ECDSA)
|
Authentication (ECDSA)
|
||||||
|
@ -929,6 +929,7 @@ if test "${with_crypto_library}" = "openssl"; then
|
|||||||
OpenSSL_version \
|
OpenSSL_version \
|
||||||
SSL_CTX_get_default_passwd_cb \
|
SSL_CTX_get_default_passwd_cb \
|
||||||
SSL_CTX_get_default_passwd_cb_userdata \
|
SSL_CTX_get_default_passwd_cb_userdata \
|
||||||
|
SSL_CTX_set1_groups \
|
||||||
SSL_CTX_set_security_level \
|
SSL_CTX_set_security_level \
|
||||||
X509_get0_notBefore \
|
X509_get0_notBefore \
|
||||||
X509_get0_notAfter \
|
X509_get0_notAfter \
|
||||||
|
@ -27,9 +27,9 @@ SSL Library information
|
|||||||
(Standalone) Show currently available hardware-based crypto acceleration
|
(Standalone) Show currently available hardware-based crypto acceleration
|
||||||
engines supported by the OpenSSL library.
|
engines supported by the OpenSSL library.
|
||||||
|
|
||||||
--show-curves
|
--show-groups
|
||||||
(Standalone) Show all available elliptic curves to use with the
|
(Standalone) Show all available elliptic curves/groups to use with the
|
||||||
``--ecdh-curve`` option.
|
``--ecdh-curve`` and ``tls-groups`` options.
|
||||||
|
|
||||||
Generating key material
|
Generating key material
|
||||||
-----------------------
|
-----------------------
|
||||||
|
@ -338,6 +338,31 @@ certificates and keys: https://github.com/OpenVPN/easy-rsa
|
|||||||
Use ``--tls-crypt`` instead if you want to use the key file to not only
|
Use ``--tls-crypt`` instead if you want to use the key file to not only
|
||||||
authenticate, but also encrypt the TLS control channel.
|
authenticate, but also encrypt the TLS control channel.
|
||||||
|
|
||||||
|
--tls-groups list
|
||||||
|
A list of allowable groups/curves in order of preference.
|
||||||
|
|
||||||
|
Set the allowed elliptic curves/groups for the TLS session.
|
||||||
|
These groups are allowed to be used in signatures and key exchange.
|
||||||
|
|
||||||
|
mbedTLS currently allows all known curves per default.
|
||||||
|
|
||||||
|
OpenSSL 1.1+ restricts the list per default to
|
||||||
|
::
|
||||||
|
|
||||||
|
"X25519:secp256r1:X448:secp521r1:secp384r1".
|
||||||
|
|
||||||
|
If you use certificates that use non-standard curves, you
|
||||||
|
might need to add them here. If you do not force the ecdh curve
|
||||||
|
by using ``--ecdh-curve``, the groups for ecdh will also be picked
|
||||||
|
from this list.
|
||||||
|
|
||||||
|
OpenVPN maps the curve name `secp256r1` to `prime256v1` to allow
|
||||||
|
specifying the same tls-groups option for mbedTLS and OpenSSL.
|
||||||
|
|
||||||
|
Warning: this option not only affects elliptic curve certificates
|
||||||
|
but also the key exchange in TLS 1.3 and using this option improperly
|
||||||
|
will disable TLS 1.3.
|
||||||
|
|
||||||
--tls-cert-profile profile
|
--tls-cert-profile profile
|
||||||
Set the allowed cryptographic algorithms for certificates according to
|
Set the allowed cryptographic algorithms for certificates according to
|
||||||
``profile``.
|
``profile``.
|
||||||
@ -368,7 +393,7 @@ certificates and keys: https://github.com/OpenVPN/easy-rsa
|
|||||||
OpenVPN will migrate to 'preferred' as default in the future. Please
|
OpenVPN will migrate to 'preferred' as default in the future. Please
|
||||||
ensure that your keys already comply.
|
ensure that your keys already comply.
|
||||||
|
|
||||||
*WARNING:* ``--tls-ciphers`` and ``--tls-ciphersuites``
|
*WARNING:* ``--tls-ciphers``, ``--tls-ciphersuites`` and ``tls-groups``
|
||||||
These options are expert features, which - if used correctly - can
|
These options are expert features, which - if used correctly - can
|
||||||
improve the security of your VPN connection. But it is also easy to
|
improve the security of your VPN connection. But it is also easy to
|
||||||
unwittingly use them to carefully align a gun with your foot, or just
|
unwittingly use them to carefully align a gun with your foot, or just
|
||||||
|
@ -183,6 +183,12 @@ SSL_CTX_get_default_passwd_cb(SSL_CTX *ctx)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* This function is implemented as macro, so the configure check for the
|
||||||
|
* function may fail, so we check for both variants here */
|
||||||
|
#if !defined(HAVE_SSL_CTX_SET1_GROUPS) && !defined(SSL_CTX_set1_groups)
|
||||||
|
#define SSL_CTX_set1_groups SSL_CTX_set1_curves
|
||||||
|
#endif
|
||||||
|
|
||||||
#if !defined(HAVE_X509_GET0_PUBKEY)
|
#if !defined(HAVE_X509_GET0_PUBKEY)
|
||||||
/**
|
/**
|
||||||
* Get the public key from a X509 certificate
|
* Get the public key from a X509 certificate
|
||||||
|
@ -7998,7 +7998,7 @@ add_option(struct options *options,
|
|||||||
VERIFY_PERMISSION(OPT_P_GENERAL);
|
VERIFY_PERMISSION(OPT_P_GENERAL);
|
||||||
options->show_tls_ciphers = true;
|
options->show_tls_ciphers = true;
|
||||||
}
|
}
|
||||||
else if (streq(p[0], "show-curves") && !p[1])
|
else if ((streq(p[0], "show-curves") || streq(p[0], "show-groups")) && !p[1])
|
||||||
{
|
{
|
||||||
VERIFY_PERMISSION(OPT_P_GENERAL);
|
VERIFY_PERMISSION(OPT_P_GENERAL);
|
||||||
options->show_curves = true;
|
options->show_curves = true;
|
||||||
@ -8006,6 +8006,9 @@ add_option(struct options *options,
|
|||||||
else if (streq(p[0], "ecdh-curve") && p[1] && !p[2])
|
else if (streq(p[0], "ecdh-curve") && p[1] && !p[2])
|
||||||
{
|
{
|
||||||
VERIFY_PERMISSION(OPT_P_GENERAL);
|
VERIFY_PERMISSION(OPT_P_GENERAL);
|
||||||
|
msg(M_WARN, "Consider setting groups/curves preference with "
|
||||||
|
"tls-groups instead of forcing a specific curve with "
|
||||||
|
"ecdh-curve.");
|
||||||
options->ecdh_curve = p[1];
|
options->ecdh_curve = p[1];
|
||||||
}
|
}
|
||||||
else if (streq(p[0], "tls-server") && !p[1])
|
else if (streq(p[0], "tls-server") && !p[1])
|
||||||
@ -8176,6 +8179,11 @@ add_option(struct options *options,
|
|||||||
VERIFY_PERMISSION(OPT_P_GENERAL);
|
VERIFY_PERMISSION(OPT_P_GENERAL);
|
||||||
options->cipher_list_tls13 = p[1];
|
options->cipher_list_tls13 = p[1];
|
||||||
}
|
}
|
||||||
|
else if (streq(p[0], "tls-groups") && p[1] && !p[2])
|
||||||
|
{
|
||||||
|
VERIFY_PERMISSION(OPT_P_GENERAL);
|
||||||
|
options->tls_groups = p[1];
|
||||||
|
}
|
||||||
else if (streq(p[0], "crl-verify") && p[1] && ((p[2] && streq(p[2], "dir"))
|
else if (streq(p[0], "crl-verify") && p[1] && ((p[2] && streq(p[2], "dir"))
|
||||||
|| !p[2]))
|
|| !p[2]))
|
||||||
{
|
{
|
||||||
|
@ -538,6 +538,7 @@ struct options
|
|||||||
bool pkcs12_file_inline;
|
bool pkcs12_file_inline;
|
||||||
const char *cipher_list;
|
const char *cipher_list;
|
||||||
const char *cipher_list_tls13;
|
const char *cipher_list_tls13;
|
||||||
|
const char *tls_groups;
|
||||||
const char *tls_cert_profile;
|
const char *tls_cert_profile;
|
||||||
const char *ecdh_curve;
|
const char *ecdh_curve;
|
||||||
const char *tls_verify;
|
const char *tls_verify;
|
||||||
|
@ -615,6 +615,12 @@ init_ssl(const struct options *options, struct tls_root_ctx *new_ctx)
|
|||||||
tls_ctx_restrict_ciphers(new_ctx, options->cipher_list);
|
tls_ctx_restrict_ciphers(new_ctx, options->cipher_list);
|
||||||
tls_ctx_restrict_ciphers_tls13(new_ctx, options->cipher_list_tls13);
|
tls_ctx_restrict_ciphers_tls13(new_ctx, options->cipher_list_tls13);
|
||||||
|
|
||||||
|
/* Set the allow groups/curves for TLS if we want to override them */
|
||||||
|
if (options->tls_groups)
|
||||||
|
{
|
||||||
|
tls_ctx_set_tls_groups(new_ctx, options->tls_groups);
|
||||||
|
}
|
||||||
|
|
||||||
if (!tls_ctx_set_options(new_ctx, options->ssl_flags))
|
if (!tls_ctx_set_options(new_ctx, options->ssl_flags))
|
||||||
{
|
{
|
||||||
goto err;
|
goto err;
|
||||||
|
@ -198,6 +198,16 @@ void tls_ctx_restrict_ciphers_tls13(struct tls_root_ctx *ctx, const char *cipher
|
|||||||
*/
|
*/
|
||||||
void tls_ctx_set_cert_profile(struct tls_root_ctx *ctx, const char *profile);
|
void tls_ctx_set_cert_profile(struct tls_root_ctx *ctx, const char *profile);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the (elliptic curve) group allowed for signatures and
|
||||||
|
* key exchange.
|
||||||
|
*
|
||||||
|
* @param ctx TLS context to restrict, must be valid.
|
||||||
|
* @param groups List of groups that will be allowed, in priority,
|
||||||
|
* separated by :
|
||||||
|
*/
|
||||||
|
void tls_ctx_set_tls_groups(struct tls_root_ctx *ctx, const char *groups);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check our certificate notBefore and notAfter fields, and warn if the cert is
|
* Check our certificate notBefore and notAfter fields, and warn if the cert is
|
||||||
* either not yet valid or has expired. Note that this is a non-fatal error,
|
* either not yet valid or has expired. Note that this is a non-fatal error,
|
||||||
|
@ -176,6 +176,11 @@ tls_ctx_free(struct tls_root_ctx *ctx)
|
|||||||
free(ctx->allowed_ciphers);
|
free(ctx->allowed_ciphers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ctx->groups)
|
||||||
|
{
|
||||||
|
free(ctx->groups);
|
||||||
|
}
|
||||||
|
|
||||||
CLEAR(*ctx);
|
CLEAR(*ctx);
|
||||||
|
|
||||||
ctx->initialised = false;
|
ctx->initialised = false;
|
||||||
@ -343,6 +348,41 @@ tls_ctx_set_cert_profile(struct tls_root_ctx *ctx, const char *profile)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
tls_ctx_set_tls_groups(struct tls_root_ctx *ctx, const char *groups)
|
||||||
|
{
|
||||||
|
ASSERT(ctx);
|
||||||
|
struct gc_arena gc = gc_new();
|
||||||
|
|
||||||
|
/* Get number of groups and allocate an array in ctx */
|
||||||
|
int groups_count = get_num_elements(groups, ':');
|
||||||
|
ALLOC_ARRAY_CLEAR(ctx->groups, mbedtls_ecp_group_id, groups_count + 1)
|
||||||
|
|
||||||
|
/* Parse allowed ciphers, getting IDs */
|
||||||
|
int i = 0;
|
||||||
|
char *tmp_groups = string_alloc(groups, &gc);
|
||||||
|
|
||||||
|
const char *token;
|
||||||
|
while ((token = strsep(&tmp_groups, ":")))
|
||||||
|
{
|
||||||
|
const mbedtls_ecp_curve_info *ci =
|
||||||
|
mbedtls_ecp_curve_info_from_name(token);
|
||||||
|
if (!ci)
|
||||||
|
{
|
||||||
|
msg(M_WARN, "Warning unknown curve/group specified: %s", token);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ctx->groups[i] = ci->grp_id;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctx->groups[i] = MBEDTLS_ECP_DP_NONE;
|
||||||
|
|
||||||
|
gc_free(&gc);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
tls_ctx_check_cert_time(const struct tls_root_ctx *ctx)
|
tls_ctx_check_cert_time(const struct tls_root_ctx *ctx)
|
||||||
{
|
{
|
||||||
@ -1043,6 +1083,11 @@ key_state_ssl_init(struct key_state_ssl *ks_ssl,
|
|||||||
mbedtls_ssl_conf_ciphersuites(ks_ssl->ssl_config, ssl_ctx->allowed_ciphers);
|
mbedtls_ssl_conf_ciphersuites(ks_ssl->ssl_config, ssl_ctx->allowed_ciphers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ssl_ctx->groups)
|
||||||
|
{
|
||||||
|
mbedtls_ssl_conf_curves(ks_ssl->ssl_config, ssl_ctx->groups);
|
||||||
|
}
|
||||||
|
|
||||||
/* Disable record splitting (for now). OpenVPN assumes records are sent
|
/* Disable record splitting (for now). OpenVPN assumes records are sent
|
||||||
* unfragmented, and changing that will require thorough review and
|
* unfragmented, and changing that will require thorough review and
|
||||||
* testing. Since OpenVPN is not susceptible to BEAST, we can just
|
* testing. Since OpenVPN is not susceptible to BEAST, we can just
|
||||||
|
@ -105,6 +105,7 @@ struct tls_root_ctx {
|
|||||||
#endif
|
#endif
|
||||||
struct external_context external_key; /**< External key context */
|
struct external_context external_key; /**< External key context */
|
||||||
int *allowed_ciphers; /**< List of allowed ciphers for this connection */
|
int *allowed_ciphers; /**< List of allowed ciphers for this connection */
|
||||||
|
mbedtls_ecp_group_id *groups; /**< List of allowed groups for this connection */
|
||||||
mbedtls_x509_crt_profile cert_profile; /**< Allowed certificate types */
|
mbedtls_x509_crt_profile cert_profile; /**< Allowed certificate types */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -233,15 +233,14 @@ ncp_get_best_cipher(const char *server_list, const char *server_cipher,
|
|||||||
|
|
||||||
char *tmp_ciphers = string_alloc(server_list, &gc_tmp);
|
char *tmp_ciphers = string_alloc(server_list, &gc_tmp);
|
||||||
|
|
||||||
const char *token = strsep(&tmp_ciphers, ":");
|
const char *token;
|
||||||
while (token)
|
while ((token = strsep(&tmp_ciphers, ":")))
|
||||||
{
|
{
|
||||||
if (tls_item_in_cipher_list(token, peer_ncp_list)
|
if (tls_item_in_cipher_list(token, peer_ncp_list)
|
||||||
|| streq(token, remote_cipher))
|
|| streq(token, remote_cipher))
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
token = strsep(&tmp_ciphers, ":");
|
|
||||||
}
|
}
|
||||||
/* We have not found a common cipher, as a last resort check if the
|
/* We have not found a common cipher, as a last resort check if the
|
||||||
* server cipher can be used
|
* server cipher can be used
|
||||||
|
@ -563,6 +563,57 @@ tls_ctx_set_cert_profile(struct tls_root_ctx *ctx, const char *profile)
|
|||||||
#endif /* ifdef HAVE_SSL_CTX_SET_SECURITY_LEVEL */
|
#endif /* ifdef HAVE_SSL_CTX_SET_SECURITY_LEVEL */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
tls_ctx_set_tls_groups(struct tls_root_ctx *ctx, const char *groups)
|
||||||
|
{
|
||||||
|
ASSERT(ctx);
|
||||||
|
struct gc_arena gc = gc_new();
|
||||||
|
/* This method could be as easy as
|
||||||
|
* SSL_CTX_set1_groups_list(ctx->ctx, groups)
|
||||||
|
* but OpenSSL does not like the name secp256r1 for prime256v1
|
||||||
|
* This is one of the important curves.
|
||||||
|
* To support the same name for OpenSSL and mbedTLS, we do
|
||||||
|
* this dance.
|
||||||
|
*/
|
||||||
|
|
||||||
|
int groups_count = get_num_elements(groups, ':');
|
||||||
|
|
||||||
|
int *glist;
|
||||||
|
/* Allocate an array for them */
|
||||||
|
ALLOC_ARRAY_CLEAR_GC(glist, int, groups_count, &gc);
|
||||||
|
|
||||||
|
/* Parse allowed ciphers, getting IDs */
|
||||||
|
int glistlen = 0;
|
||||||
|
char *tmp_groups = string_alloc(groups, &gc);
|
||||||
|
|
||||||
|
const char *token;
|
||||||
|
while ((token = strsep(&tmp_groups, ":")))
|
||||||
|
{
|
||||||
|
if (streq(token, "secp256r1"))
|
||||||
|
{
|
||||||
|
token = "prime256v1";
|
||||||
|
}
|
||||||
|
int nid = OBJ_sn2nid(token);
|
||||||
|
|
||||||
|
if (nid == 0)
|
||||||
|
{
|
||||||
|
msg(M_WARN, "Warning unknown curve/group specified: %s", token);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
glist[glistlen] = nid;
|
||||||
|
glistlen++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!SSL_CTX_set1_groups(ctx->ctx, glist, glistlen))
|
||||||
|
{
|
||||||
|
crypto_msg(M_FATAL, "Failed to set allowed TLS group list: %s",
|
||||||
|
groups);
|
||||||
|
}
|
||||||
|
gc_free(&gc);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
tls_ctx_check_cert_time(const struct tls_root_ctx *ctx)
|
tls_ctx_check_cert_time(const struct tls_root_ctx *ctx)
|
||||||
{
|
{
|
||||||
@ -2135,6 +2186,8 @@ show_available_tls_ciphers_list(const char *cipher_list,
|
|||||||
void
|
void
|
||||||
show_available_curves(void)
|
show_available_curves(void)
|
||||||
{
|
{
|
||||||
|
printf("Consider using openssl 'ecparam -list_curves' as\n"
|
||||||
|
"alternative to running this command.\n");
|
||||||
#ifndef OPENSSL_NO_EC
|
#ifndef OPENSSL_NO_EC
|
||||||
EC_builtin_curve *curves = NULL;
|
EC_builtin_curve *curves = NULL;
|
||||||
size_t crv_len = 0;
|
size_t crv_len = 0;
|
||||||
@ -2144,7 +2197,7 @@ show_available_curves(void)
|
|||||||
ALLOC_ARRAY(curves, EC_builtin_curve, crv_len);
|
ALLOC_ARRAY(curves, EC_builtin_curve, crv_len);
|
||||||
if (EC_get_builtin_curves(curves, crv_len))
|
if (EC_get_builtin_curves(curves, crv_len))
|
||||||
{
|
{
|
||||||
printf("Available Elliptic curves:\n");
|
printf("\nAvailable Elliptic curves/groups:\n");
|
||||||
for (n = 0; n < crv_len; n++)
|
for (n = 0; n < crv_len; n++)
|
||||||
{
|
{
|
||||||
const char *sname;
|
const char *sname;
|
||||||
|
Loading…
Reference in New Issue
Block a user