Project

General

Profile

Actions

Bug #12324

closed

Support OpenSSL 1.1.0 (and drop support for 0.9.6/0.9.7)

Added by rhenium (Kazuki Yamaguchi) over 8 years ago. Updated almost 8 years ago.

Status:
Closed
Target version:
-
[ruby-core:75225]

Description

The last beta for OpenSSL1.1.0 (1.1.0-pre5) was released a week ago. According to OpenSSL's website[1], OpenSSL 1.1.0 final will be released on 2016-05-12.
OpenSSL 1.1.0 introduces many many compatibility breaking changes[2][3] but they are almost done and I think we can start work on it.

Also, I think we can drop support for ancient (-0.9.7) versions of OpenSSL. This can save many compatibility macros (about 700 lines). Considering the last official release is over 9 years ago (2007-02-23) and even 0.9.8/1.0.0 series are no longer supported[1], there should be no reason to continue supporting them. In fact builds with OpenSSL 0.9.7 are already broken since r40461 (2013-04-25, Ruby 2.1.0) but it looks like nobody noticed it. (I personally think all unsupported version should be removed but 0.9.8 is unfortunately still widely used...)

[1] https://www.openssl.org/policies/releasestrat.html
[2] https://www.openssl.org/news/openssl-1.1.0-notes.html
[3] https://www.openssl.org/news/changelog.html


I found some incompatibilities in OpenSSL 1.1.0 which affect Ruby-world API:

  • SSL_CTX_set_tmp_ecdh_callback() is removed

    OpenSSL::SSL::SSLContext#tmp_ecdh_callback (exists in Ruby 2.3, [Feature #11356]) breaks. This is used to enable ECDH and specify the curve to be used.
    OpenSSL 1.0.2 and later provide SSL_CTX_set1_curves_list(). This sets the "supported (named) curves" and OpenSSL selects the most appropriate curve from the list. The automatic selection is enabled by SSL_CTX_set_ecdh_auto() on 1.0.2. It is always enabled on 1.1.0.

    Summarizing... ECDH is enabled:

    • OpenSSL 1.1.0: always.
    • OpenSSL 1.0.2, LibreSSL 2.3: if SSL_CTX_set_ecdh_auto() is called.
    • OpenSSL -1.0.2, LibreSSL 2.3: if SSL_CTX_set_tmp_ecdh{_callback,}() is called.

    The curve to be used is:

    • OpenSSL 1.0.2/1.1.0: automatically selected by OpenSSL, if enabled. The "supported curves" can be changed by SSL_CTX_set1_curves{_list,}().
    • LibreSSL 2.3: automatically selected if enabled. The list can't be changed.
    • OpenSSL -1.0.2, LibreSSL 2.3: set by SSL_CTX_set_tmp_ecdh{_callback,}().

    In my patch, I made this deprecated (rb_warn() on SSLContext#setup) it and added SSLContext#set_ecdh_curves:

    • OpenSSL 1.0.2/1.1.0: wraps SSL_CTX_set1_curves_list()
    • OpenSSL -1.0.1, LibreSSL 2.3: wraps SSL_CTX_set_tmp_ecdh() (so only 1 curve can be set)
    # assume the client supports P-224 and P-521
    ctx = OpenSSL::SSL::SSLContext.new
    ctx.set_ecdh_curves "P-384:P-224:P-521"
    svr = OpenSSL::SSL::SSLServer.new(sock, ctx)
    svr.accept # will use P-224
    
  • Accessors for OpenSSL::PKey::{DH,DSA,RSA}'s paramters/keys

    Since most structures of OpenSSL are made opaque, we can no longer set directly these values. OpenSSL 1.1.0 provides setter functions (e.g. DH_set0_key()) but they are not equivalent; they require setting all relevant values at the same time. For example, such code (4 usage in tests) is no longer possible:

    dh = OpenSSL::PKey::DH.new(File.read("dhparams.pem"))
    dh.priv_key = OpenSSL::BN.new("000..")
    

    In my patch I deprecated them and added XX#set_XXX methods (such as DH#set_key, which sets pub_key and priv_key).

    dh = OpenSSL::PKey::DH.new(File.read("dhparams.pem"))
    dh.set_key(OpenSSL::BN.new("000.."), # pub_key
               OpenSSL::BN.new("000..")) # priv_key
    

    This is not beautiful but it looks like there is no other way for this...

  • Security level

    Please see the manpage SSL_CTX_set_security_level(3):

    https://www.openssl.org/docs/manmaster/ssl/SSL_CTX_set_security_level.html

    OpenSSL 1.1.0 introduces "security level", which disables insecure settings, such as insecure ciphers, depending on the level. The default level is 1 and this means:

    The security level corresponds to a minimum of 80 bits of security. Any parameters offering below 80 bits of security are excluded. As a result RSA, DSA and DH keys shorter than 1024 bits and ECC keys shorter than 160 bits are prohibited. All export ciphersuites are prohibited since they all offer less than 80 bits of security. SSL version 2 is prohibited. Any ciphersuite using MD5 for the MAC is also prohibited.
    (from 1.1.0-pre5 doc)

    New methods would be essential. Actually this breaks many tests which use aNULL cipher suites.

    I added SSLContext#security_level and SSLContext#security_level= in my patch.


Here is my try. Because of the amount of OpenSSL changes, almost all .c files had to be modified. Roughly splitted into 38 commits:

https://github.com/ruby/ruby/compare/trunk...rhenium:feature/openssl-110-v1
(the attached gzip'ed is the same)

This includes:

  • Add check for SSL_CTX_clear_options(), which doesn't exist in OpenSSL 0.9.8l and older.
  • Drop support for 0.9.7.
  • Remove OPENSSL_NO_HMAC support as it have never worked.
  • Deprecate (rb_warn()ing) SSLContext#tmp_ecdh_callback and add SSL::SSLContext#set_ecdh_curves method.
  • Deprecate parameters/keys setter for PKey::DH, PKey::RSA and PKey::DSA, and add PKey::XX#set_XXX methods.
  • Add SSL::SSLContext#security_level and #security_level=.
  • Test fixes for open-uri and rubygems (regenerate the test certificates; they are incorrectly created and OpenSSL 1.1.0 starts to complain about it).

This compiles and tests pass with the following versions (on my Linux/gcc environment):

  • 0.9.8zh
  • 1.0.0t
  • 1.0.1s
  • 1.0.2g
  • OpenSSL master
  • LibreSSL 2.3.3

Note that this doesn't compile with 1.1.0-pre5; OpenSSL GH-975 is required (already in master and the 1.1.0 final will include this).
https://github.com/openssl/openssl/pull/975

The patch is far from perfect - I'm not sure about the new methods and also probably there are bugs.

Any comments are welcome, I'll update the patch.

Thanks,


Files

patches-v1.tar.gz (81.4 KB) patches-v1.tar.gz rhenium (Kazuki Yamaguchi), 04/27/2016 05:09 PM
patches-v2.tar.gz (79.9 KB) patches-v2.tar.gz rhenium (Kazuki Yamaguchi), 05/05/2016 09:56 AM
interdiff-v1-v2.patch (13.4 KB) interdiff-v1-v2.patch rhenium (Kazuki Yamaguchi), 05/05/2016 09:56 AM
patches-v3.tar.gz (80.1 KB) patches-v3.tar.gz rhenium (Kazuki Yamaguchi), 05/14/2016 11:20 AM
interdiff-v2-v3.patch (15.7 KB) interdiff-v2-v3.patch rhenium (Kazuki Yamaguchi), 05/14/2016 11:20 AM
Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0