Skip to content

Commit

Permalink
Allow DEFAULT in data-ciphers and report both expanded and user set o…
Browse files Browse the repository at this point in the history
…ption

This adds support for parsing DEFAULT in data-ciphers, the idea is that people
can modify the default without repeating the default ciphers.

In the past we have seem that people will use data-ciphers BF-CBC or
data-ciphers AES-128-CBC when getting the warning that the cipher is not
supported by the server.  This commit aims to provide a better way for
these situation as we still want people to rely on default cipher selection
from OpenVPN when possible.

Change-Id: Ia1c5209022d3ab4c0dac6438c41891c7d059f812
  • Loading branch information
schwabe committed Dec 10, 2024
1 parent ae82631 commit d3073cb
Show file tree
Hide file tree
Showing 8 changed files with 276 additions and 48 deletions.
5 changes: 5 additions & 0 deletions Changes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ Support for tun/tap via unix domain socket and lwipovpn support

For more details see [lwipovpn on Gihtub](https://github.com/OpenVPN/lwipovpn).

Default ciphers in ``--data-ciphers``
Ciphers in ``--data-ciphers`` can contain the string DEFAULT that is
replaced by the default ciphers used by OpenVPN, making it easier to
add an allowed cipher without having to spell out the default ciphers.

Deprecated features
-------------------
``secret`` support has been removed by default.
Expand Down
6 changes: 6 additions & 0 deletions doc/man-sections/protocol-options.rst
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,12 @@ configured in a compatible way between both the local and remote side.
Chacha20-Poly1305 if the underlying SSL library (and its configuration)
supports it.

Starting with OpenVPN 2.7 the special keyword DEFAULT can be used in the
string and is replaced by the default ciphers. This can be used add
an additional allowed cipher to the allowed ciphers, e.g.
:code:`DEFAULT:AES-192-CBC` to use the default ciphers but also allow
:code:`AES-192-CBC`.

Cipher negotiation is enabled in client-server mode only. I.e. if
``--mode`` is set to `server` (server-side, implied by setting
``--server`` ), or if ``--pull`` is specified (client-side, implied by
Expand Down
9 changes: 5 additions & 4 deletions src/openvpn/multi.c
Original file line number Diff line number Diff line change
Expand Up @@ -1896,14 +1896,15 @@ multi_client_set_protocol_options(struct context *c)
if (strlen(peer_ciphers) > 0)
{
msg(M_INFO, "PUSH: No common cipher between server and client. "
"Server data-ciphers: '%s', client supported ciphers '%s'",
o->ncp_ciphers, peer_ciphers);
"Server data-ciphers: '%s%s', client supported ciphers '%s'",
o->ncp_ciphers_conf, ncp_expanded_ciphers(o, &gc), peer_ciphers);
}
else if (tls_multi->remote_ciphername)
{
msg(M_INFO, "PUSH: No common cipher between server and client. "
"Server data-ciphers: '%s', client supports cipher '%s'",
o->ncp_ciphers, tls_multi->remote_ciphername);
"Server data-ciphers: '%s'%s, client supports cipher '%s'",
o->ncp_ciphers_conf, ncp_expanded_ciphers(o, &gc),
tls_multi->remote_ciphername);
}
else
{
Expand Down
39 changes: 3 additions & 36 deletions src/openvpn/options.c
Original file line number Diff line number Diff line change
Expand Up @@ -3486,40 +3486,6 @@ options_postprocess_verify(const struct options *o)
}
}


/**
* Checks for availibility of Chacha20-Poly1305 and sets
* the ncp_cipher to either AES-256-GCM:AES-128-GCM or
* AES-256-GCM:AES-128-GCM:CHACHA20-POLY1305.
*/
static void
options_postprocess_setdefault_ncpciphers(struct options *o)
{
if (o->ncp_ciphers)
{
/* custom --data-ciphers set, keep list */
return;
}

/* check if crypto library supports chacha */
bool can_do_chacha = cipher_valid("CHACHA20-POLY1305");

if (can_do_chacha && dco_enabled(o))
{
/* also make sure that dco supports chacha */
can_do_chacha = tls_item_in_cipher_list("CHACHA20-POLY1305", dco_get_supported_ciphers());
}

if (can_do_chacha)
{
o->ncp_ciphers = "AES-256-GCM:AES-128-GCM:CHACHA20-POLY1305";
}
else
{
o->ncp_ciphers = "AES-256-GCM:AES-128-GCM";
}
}

static void
options_postprocess_cipher(struct options *o)
{
Expand Down Expand Up @@ -3550,15 +3516,16 @@ options_postprocess_cipher(struct options *o)
"defaulted to BF-CBC as fallback when cipher negotiation "
"failed in this case. If you need this fallback please add "
"'--data-ciphers-fallback BF-CBC' to your configuration "
"and/or add BF-CBC to --data-ciphers.");
"and/or add BF-CBC to --data-ciphers. E.g. "
"--data-ciphers %s:BF-CBC", o->ncp_ciphers_conf);
}
else if (!o->enable_ncp_fallback
&& !tls_item_in_cipher_list(o->ciphername, o->ncp_ciphers))
{
msg(M_WARN, "DEPRECATED OPTION: --cipher set to '%s' but missing in "
"--data-ciphers (%s). OpenVPN ignores --cipher for cipher "
"negotiations. ",
o->ciphername, o->ncp_ciphers);
o->ciphername, o->ncp_ciphers_conf);
}
}

Expand Down
2 changes: 2 additions & 0 deletions src/openvpn/options.h
Original file line number Diff line number Diff line change
Expand Up @@ -559,6 +559,8 @@ struct options
const char *ciphername;
bool enable_ncp_fallback; /**< If defined fall back to
* ciphername if NCP fails */
/** The original ncp_ciphers specified by the user in the configuration*/
const char *ncp_ciphers_conf;
const char *ncp_ciphers;
const char *authname;
const char *engine;
Expand Down
115 changes: 108 additions & 7 deletions src/openvpn/ssl_ncp.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
#include "config.h"
#endif

#include <string.h>

#include "syshead.h"
#include "win32.h"

Expand Down Expand Up @@ -222,7 +224,6 @@ tls_item_in_cipher_list(const char *item, const char *list)

return token != NULL;
}

const char *
tls_peer_ncp_list(const char *peer_info, struct gc_arena *gc)
{
Expand Down Expand Up @@ -334,15 +335,16 @@ check_pull_client_ncp(struct context *c, const int found)
return true;
}

/* We failed negotiation, give appropiate error message */
/* We failed negotiation, give appropriate error message */
if (c->c2.tls_multi->remote_ciphername)
{
msg(D_TLS_ERRORS, "OPTIONS ERROR: failed to negotiate "
"cipher with server. Add the server's "
"cipher ('%s') to --data-ciphers (currently '%s') if "
"you want to connect to this server.",
"cipher ('%s') to --data-ciphers (currently '%s'), e.g."
"--data-ciphers %s:%s if you want to connect to this server.",
c->c2.tls_multi->remote_ciphername,
c->options.ncp_ciphers);
c->options.ncp_ciphers_conf, c->options.ncp_ciphers_conf,
c->c2.tls_multi->remote_ciphername);
return false;

}
Expand Down Expand Up @@ -516,14 +518,113 @@ check_session_cipher(struct tls_session *session, struct options *options)
if (!session->opt->server && !cipher_allowed_as_fallback
&& !tls_item_in_cipher_list(options->ciphername, options->ncp_ciphers))
{
msg(D_TLS_ERRORS, "Error: negotiated cipher not allowed - %s not in %s",
options->ciphername, options->ncp_ciphers);
struct gc_arena gc = gc_new();
msg(D_TLS_ERRORS, "Error: negotiated cipher not allowed - %s not in %s%s",
options->ciphername, options->ncp_ciphers_conf,
ncp_expanded_ciphers(options, &gc));
/* undo cipher push, abort connection setup */
options->ciphername = session->opt->config_ciphername;
gc_free(&gc);
return false;
}
else
{
return true;
}
}

/**
* Replaces the string DEFAULT with the string \param replace. The
* @param o Options struct to modify and to use the gc from
* @param replace string used to replace the DEFAULT string
*/
static void
replace_default_in_ncp_ciphers_option(struct options *o, const char *replace)
{
const char *search = "DEFAULT";
const int ncp_ciphers_len = strlen(o->ncp_ciphers) + strlen(replace) - strlen(search) + 1;

uint8_t *ncp_ciphers = gc_malloc(ncp_ciphers_len, true, &o->gc);

struct buffer ncp_ciphers_buf;
buf_set_write(&ncp_ciphers_buf, ncp_ciphers, ncp_ciphers_len);

const char *def = strstr(o->ncp_ciphers, search);

/* Copy everything before the DEFAULT string */
buf_write(&ncp_ciphers_buf, o->ncp_ciphers, def - o->ncp_ciphers);

/* copy the default string. */
buf_write(&ncp_ciphers_buf, replace, strlen(replace));

/* copy the rest of the ncp cipher string */
const char *after_default = def + strlen(search);
buf_write(&ncp_ciphers_buf, after_default, strlen(after_default));

o->ncp_ciphers = (char *) ncp_ciphers;
}

/**
* Checks for availibility of Chacha20-Poly1305 and sets
* the ncp_cipher to either AES-256-GCM:AES-128-GCM or
* AES-256-GCM:AES-128-GCM:CHACHA20-POLY1305.
*/
void
options_postprocess_setdefault_ncpciphers(struct options *o)
{
bool default_in_cipher_list = o->ncp_ciphers
&& tls_item_in_cipher_list("DEFAULT", o->ncp_ciphers);

/* preserve the values that the user put into the configuration */
o->ncp_ciphers_conf = o->ncp_ciphers;

/* check if crypto library supports chacha */
bool can_do_chacha = cipher_valid("CHACHA20-POLY1305");

if (can_do_chacha && dco_enabled(o))
{
/* also make sure that dco supports chacha */
can_do_chacha = tls_item_in_cipher_list("CHACHA20-POLY1305", dco_get_supported_ciphers());
}

const char *default_ciphers = "AES-256-GCM:AES-128-GCM:CHACHA20-POLY1305";

if (!can_do_chacha)
{
default_ciphers = "AES-256-GCM:AES-128-GCM";
}

/* want to rather print DEFAULT instead of a manually set default list */
if (!o->ncp_ciphers_conf || !strcmp(default_ciphers, o->ncp_ciphers_conf))
{
o->ncp_ciphers = default_ciphers;
o->ncp_ciphers_conf = "DEFAULT";
}
else if (!default_in_cipher_list)
{
/* custom cipher list without DEFAULT string in it,
* nothing to replace/mutate */
return;
}
else
{
replace_default_in_ncp_ciphers_option(o, default_ciphers);
}
}

const char *
ncp_expanded_ciphers(struct options *o, struct gc_arena *gc)
{
if (!strcmp(o->ncp_ciphers, o->ncp_ciphers_conf))
{
/* expanded ciphers and user set ciphers are identical, no need to
* add an expanded version */
return "";
}

/* two extra brackets, one space, NUL byte */
struct buffer expanded_ciphers_buf = alloc_buf_gc(strlen(o->ncp_ciphers) + 4, gc);

buf_printf(&expanded_ciphers_buf, " (%s)", o->ncp_ciphers);
return BSTR(&expanded_ciphers_buf);
}
20 changes: 20 additions & 0 deletions src/openvpn/ssl_ncp.h
Original file line number Diff line number Diff line change
Expand Up @@ -156,4 +156,24 @@ get_p2p_ncp_cipher(struct tls_session *session, const char *peer_info,
bool
check_session_cipher(struct tls_session *session, struct options *options);

/**
* Checks for availability of Chacha20-Poly1305 and sets
* the ncp_cipher to either AES-256-GCM:AES-128-GCM or
* AES-256-GCM:AES-128-GCM:CHACHA20-POLY1305 if not set.
*
* If DEFAULT is in the ncp_cipher string, it will be replaced
* by the default cipher string as defined above.
*/
void
options_postprocess_setdefault_ncpciphers(struct options *o);

/** returns the o->ncp_ciphers in brackets, e.g.
* (AES-256-GCM:CHACHA20-POLY1305) if o->ncp_ciphers_conf
* and o->ncp_ciphers differ, otherwise an empty string
*
* The returned string will be allocated in the passed \param gc
*
*/
const char *
ncp_expanded_ciphers(struct options *o, struct gc_arena *gc);
#endif /* ifndef OPENVPN_SSL_NCP_H */
Loading

0 comments on commit d3073cb

Please sign in to comment.