On 14/06/2013 02:47, Joachim Schipper wrote:
From James Yonan <ja...@openvpn.net>:
TLS Protocol
------------
Since day 1, OpenVPN has used TLS 1.0 as a control channel and key
exchange mechanism. But now we have TLS 1.1 and 1.2, each of which
addresses significant shortcomings in its predecessor. Fortunately,
SSL/TLS already includes dynamic version negotiation. So I've put
together a patch that leverages on this, by allowing OpenVPN client
and server to negotiate the TLS version and use the highest version
supported by both peers. The patch also adds a new directive "tls-
version-min" that allows either client or server to specify a minimum
required TLS version from the peer.
https://github.com/jamesyonan/openvpn/commit/6ee8faade224cc346d67a7f17
1
6df4012782999a
Some comments on the design and implementation here. (I've just looked at the
code, not tested it.)
I'm confused by your comments about TLS/SSL versions used. Our own builds using PolarSSL do
negotiate TLSv1.1 and TLSv1.2 between themselves and when interoperating with OpenSSL, logging
"Control Channel: TLSv1.1, cipher XXX" or even "Control Channel: TLSv1.2, cipher
XXX". In general, the highest version supported by both client and server is used, as is
normally the case with SSL. However, stock Ubuntu OpenVPN seems to negotiate TLSv1. Huh?
PolarSSL dynamically negotiates the TLS version within the bounds set by
ssl_set_min_version and ssl_set_max_version. If these methods haven't
been called, as is the case with OpenVPN 2.3.2 and earlier, then the
highest version supported by both peers will be used. So even in the
absence of my patch, PolarSSL if used by both client and server, could
negotiate a higher version than TLS 1.0.
OpenSSL, however, does not appear to negotiate a version other than TLS
1.0 if TLSv1_server_method() or TLSv1_client_method() is passed to
SSL_CTX_new, as is done by all versions of OpenVPN up to 2.3.2.
To get the adaptive versioning behavior in OpenSSL, you have to use
SSLv23_server_method() or SSLv23_client_method() and then explicitly
disable the versions you don't want to consider, i.e. SSL_OP_NO_SSLv2,
SSL_OP_NO_SSLv3, SSL_OP_NO_TLSv1, SSL_OP_NO_TLSv1_1, etc.
I'm not sure what purpose the "or-highest" serves. Clients already connect with the
newest protocol supported by both client and server; if you want to run a TLSv1.2-only network,
just set min-version to 1.2 on the server. If you're an especially competent and paranoid user, you
might want to set min-version on the client, but in that case you hardly need an
"or-highest", since you know which cipher suites your client supports.
Suppose a server admin wants to upgrade to TLS 1.2 over some transition
period, to allow time to upgrade existing clients in the field.
At the end of the transition period, tls-version-min 1.2 would be added
to the server config, but this can't be done immediately or it might
lock out older clients (using TLS 1.0) before they have a chance to upgrade.
So a transition plan could start by distributing client config files
containing:
setenv opt tls-version-min 1.2 or-highest
Older OpenVPN versions that don't understand tls-version-min would
simply ignore it, under the presumption that these clients would be
upgraded before the end of the transition period.
Newer versions of OpenVPN that understand tls-version-min and are linked
with SSL libraries that support TLS 1.2 would force a TLS 1.2 connection
with server, so they would be immune to any version rollback attack
where a MiTM tries to force a lower version negotiation.
However there is a middle ground here: what about OpenVPN clients that
understand tls-version-min but are not linked with an SSL library that
implements TLS 1.2. The "or-highest" tells the client to use the
highest TLS version supported by their SSL library as the
tls-version-min parameter.
If "or-highest" wasn't used, and the SSL lib doesn't implement TLS 1.2,
then an error would be given that TLS 1.2 is an unrecognized TLS version.
The overall goal here is to provide tools that allow a controlled
rollout of TLS 1.2 that doesn't break any clients during the rollout
period, and to upgrade to 1.2 in a way that doesn't create the potential
for MiTM version rollback attacks.
Some of the whitespace looks odd/wrong.
Ok.
Does the change to key_state_ssl_init() do anything?
I had to add ssl_flags (containing tls-version-min parameter) to
key_state_ssl_init because that seems like the most appropriate place to
configure tls-version-min for PolarSSL.
For OpenSSL, tls_ctx_set_options is the right place to configure
tls-version-min.
The variables polar_{major,minor} in ssl_polarssl.c should be renamed, probably
to something like ssl_{major,minor}_version - it's not a PolarSSL version.
Sure.
The switch(tls_version_min) needs a default case, just compile the first
case/default: unconditionally.
There is a default case already -- it's right under "case TLS_VER_1_0:".
OpenVPN Protocol
----------------
[Handshaking] could be used to negotiate other protocol capabilities
such as cipher and HMAC digest:
IV_CIPHER=BF-CBC,AES-128-CBC
IV_AUTH=SHA1,SHA256,SHA512
(...) There
is also the issue of the size of the Client Capabilities List. The
IV_CIPHER and IV_AUTH lists might grow to be quite long (...) To
reduce the size, we might
consider:
(a) use a single character designation for ciphers and HMAC digests,
such as:
Cipher Codes:
A : BF-CBC
B : AES-128-CBC
. . .
Then communicate IV_CIPHER using the cipher codes:
IV_CIPHER=AB
This would require that the OpenVPN Project standardize on set of
codes for ciphers and HMAC digests.
This is a good idea in general, but why not use SSL cipher suites (which already have standardized
binary representations)? That has the added advantage of itting AEAD suites like AES-GCM, which are
getting more important. (We may need to add some, but note that the { 0xff, X }
"namespace" is available for "local and experimental" cipher suites.)
I do like the idea of using SSL ciphersuites. There are two ways this
could be done:
(1) extract cipher/auth parameters from the ciphersuite name, using some
sort of lookup table. The problem here is that some SSL ciphersuites
might not be implemented yet in OpenVPN. For example, if the TLS
ciphersuite is AES-GCM, it would require a version of OpenVPN that
understands GCM mode. Another way to handle this would be to have the
OpenVPN peer limit the ciphersuites it advertises/accepts to those which
have a supported cipher/auth translation in OpenVPN.
(2) have OpenVPN actually encrypt/decrypt/MAC data channel packets using
the ciphersuite algorithm implementation in the underlying SSL library.
The main issue here is that SSL implementations don't generally expose
an API allowing library clients to directly access the ciphersuite
algorithm implementations.
Negotiating ciphers might be fatal in some configurations that were a bad idea
to begin with. E.g. if you use OpenVPN with a static key and --auth none (which
is a bad idea!), adding this negotation could allow an attacker to set the
cipher to none or, more dangerously, to a very weak cipher like DES (provided
it is mutually supported). An attacker could then bruteforce the key and use
his knowledge of 56 bits of the key to attack stronger protocols like AES or
3DES. (Or do we only negotiate in SSL mode? I must admit to being fuzzy on the
non-SSL mode.)
Static key mode has no negotiation.
Agreed that any negotiation model must have constraints placed on the
security of the negotiated cipher and HMAC digest. You would probably
want to disable "cipher none" or "auth none" on the presumption that
users who want a cleartext tunnel would explicitly configure the client
and server for this.
One thing to keep in mind is that OpenVPN protocol negotiation occurs
over the already-negotiated TLS session, so it is immune to being
tampered with by a MiTM. This is in contrast to SSL/TLS where a MiTM
can affect the negotiation.
James