From 2bca463f44d9173ec85a225d482bb2f3a634bf95 Mon Sep 17 00:00:00 2001 From: Thiruvadi Rajaraman Date: Sat, 4 Nov 2017 10:30:06 -0700 Subject: ruby: Security fix for CVE-2016-7798 affectes ruby < 2.3.1 Signed-off-by: Thiruvadi Rajaraman Signed-off-by: Armin Kuster (cherry picked from commit 6af2319008dc16c61092f71ff227c285aac51288) Signed-off-by: Armin Kuster --- .../recipes-devtools/ruby/ruby/CVE-2016-7798.patch | 164 +++++++++++++++++++++ meta/recipes-devtools/ruby/ruby_2.2.5.bb | 4 +- 2 files changed, 167 insertions(+), 1 deletion(-) create mode 100644 meta/recipes-devtools/ruby/ruby/CVE-2016-7798.patch diff --git a/meta/recipes-devtools/ruby/ruby/CVE-2016-7798.patch b/meta/recipes-devtools/ruby/ruby/CVE-2016-7798.patch new file mode 100644 index 0000000000..2b8772ba41 --- /dev/null +++ b/meta/recipes-devtools/ruby/ruby/CVE-2016-7798.patch @@ -0,0 +1,164 @@ +cipher: don't set dummy encryption key in Cipher#initialize +Remove the encryption key initialization from Cipher#initialize. This +is effectively a revert of r32723 ("Avoid possible SEGV from AES +encryption/decryption", 2011-07-28). + +r32723, which added the key initialization, was a workaround for +Ruby Bug #2768. For some certain ciphers, calling EVP_CipherUpdate() +before setting an encryption key caused segfault. It was not a problem +until OpenSSL implemented GCM mode - the encryption key could be +overridden by repeated calls of EVP_CipherInit_ex(). But, it is not the +case for AES-GCM ciphers. Setting a key, an IV, a key, in this order +causes the IV to be reset to an all-zero IV. + +The problem of Bug #2768 persists on the current versions of OpenSSL. +So, make Cipher#update raise an exception if a key is not yet set by the +user. Since encrypting or decrypting without key does not make any +sense, this should not break existing applications. + +Users can still call Cipher#key= and Cipher#iv= multiple times with +their own responsibility. + +Reference: https://bugs.ruby-lang.org/issues/2768 +Reference: https://bugs.ruby-lang.org/issues/8221 + +Upstream-Status: Backport +CVE: CVE-2016-7798 + +Signed-off-by: Thiruvadi Rajaraman + +Index: ruby-2.2.2/ext/openssl/ossl_cipher.c +=================================================================== +--- ruby-2.2.2.orig/ext/openssl/ossl_cipher.c ++++ ruby-2.2.2/ext/openssl/ossl_cipher.c +@@ -35,6 +35,7 @@ + */ + VALUE cCipher; + VALUE eCipherError; ++static ID id_key_set; + + static VALUE ossl_cipher_alloc(VALUE klass); + static void ossl_cipher_free(void *ptr); +@@ -119,7 +120,6 @@ ossl_cipher_initialize(VALUE self, VALUE + EVP_CIPHER_CTX *ctx; + const EVP_CIPHER *cipher; + char *name; +- unsigned char key[EVP_MAX_KEY_LENGTH]; + + name = StringValuePtr(str); + GetCipherInit(self, ctx); +@@ -131,14 +131,7 @@ ossl_cipher_initialize(VALUE self, VALUE + if (!(cipher = EVP_get_cipherbyname(name))) { + ossl_raise(rb_eRuntimeError, "unsupported cipher algorithm (%s)", name); + } +- /* +- * The EVP which has EVP_CIPH_RAND_KEY flag (such as DES3) allows +- * uninitialized key, but other EVPs (such as AES) does not allow it. +- * Calling EVP_CipherUpdate() without initializing key causes SEGV so we +- * set the data filled with "\0" as the key by default. +- */ +- memset(key, 0, EVP_MAX_KEY_LENGTH); +- if (EVP_CipherInit_ex(ctx, cipher, NULL, key, NULL, -1) != 1) ++ if (EVP_CipherInit_ex(ctx, cipher, NULL, NULL, NULL, -1) != 1) + ossl_raise(eCipherError, NULL); + + return self; +@@ -256,6 +249,8 @@ ossl_cipher_init(int argc, VALUE *argv, + if (EVP_CipherInit_ex(ctx, NULL, NULL, p_key, p_iv, mode) != 1) { + ossl_raise(eCipherError, NULL); + } ++ if (p_key) ++ rb_ivar_set(self, id_key_set, Qtrue); + + return self; + } +@@ -343,6 +338,8 @@ ossl_cipher_pkcs5_keyivgen(int argc, VAL + OPENSSL_cleanse(key, sizeof key); + OPENSSL_cleanse(iv, sizeof iv); + ++ rb_ivar_set(self, id_key_set, Qtrue); ++ + return Qnil; + } + +@@ -396,6 +393,9 @@ ossl_cipher_update(int argc, VALUE *argv + + rb_scan_args(argc, argv, "11", &data, &str); + ++ if (!RTEST(rb_attr_get(self, id_key_set))) ++ ossl_raise(eCipherError, "key not set"); ++ + StringValue(data); + in = (unsigned char *)RSTRING_PTR(data); + if ((in_len = RSTRING_LEN(data)) == 0) +@@ -495,6 +495,8 @@ ossl_cipher_set_key(VALUE self, VALUE ke + if (EVP_CipherInit_ex(ctx, NULL, NULL, (unsigned char *)RSTRING_PTR(key), NULL, -1) != 1) + ossl_raise(eCipherError, NULL); + ++ rb_ivar_set(self, id_key_set, Qtrue); ++ + return key; + } + +@@ -1013,5 +1015,7 @@ Init_ossl_cipher(void) + rb_define_method(cCipher, "iv_len", ossl_cipher_iv_length, 0); + rb_define_method(cCipher, "block_size", ossl_cipher_block_size, 0); + rb_define_method(cCipher, "padding=", ossl_cipher_set_padding, 1); ++ ++ id_key_set = rb_intern_const("key_set"); + } + +Index: ruby-2.2.2/test/openssl/test_cipher.rb +=================================================================== +--- ruby-2.2.2.orig/test/openssl/test_cipher.rb ++++ ruby-2.2.2/test/openssl/test_cipher.rb +@@ -80,6 +80,7 @@ class OpenSSL::TestCipher < Test::Unit:: + + def test_empty_data + @c1.encrypt ++ @c1.random_key + assert_raise(ArgumentError){ @c1.update("") } + end + +@@ -127,13 +128,10 @@ class OpenSSL::TestCipher < Test::Unit:: + assert_equal(pt, c2.update(ct) + c2.final) + } + end +- +- def test_AES_crush +- 500.times do +- assert_nothing_raised("[Bug #2768]") do +- # it caused OpenSSL SEGV by uninitialized key +- OpenSSL::Cipher::AES128.new("ECB").update "." * 17 +- end ++ def test_update_raise_if_key_not_set ++ assert_raise(OpenSSL::Cipher::CipherError) do ++ # it caused OpenSSL SEGV by uninitialized key [Bug #2768] ++ OpenSSL::Cipher::AES128.new("ECB").update "." * 17 + end + end + end +@@ -236,6 +234,23 @@ class OpenSSL::TestCipher < Test::Unit:: + end + + end ++ def test_aes_gcm_key_iv_order_issue ++ pt = "[ruby/openssl#49]" ++ cipher = OpenSSL::Cipher.new("aes-128-gcm").encrypt ++ cipher.key = "x" * 16 ++ cipher.iv = "a" * 12 ++ ct1 = cipher.update(pt) << cipher.final ++ tag1 = cipher.auth_tag ++ ++ cipher = OpenSSL::Cipher.new("aes-128-gcm").encrypt ++ cipher.iv = "a" * 12 ++ cipher.key = "x" * 16 ++ ct2 = cipher.update(pt) << cipher.final ++ tag2 = cipher.auth_tag ++ ++ assert_equal ct1, ct2 ++ assert_equal tag1, tag2 ++ end if has_cipher?("aes-128-gcm") + + private + diff --git a/meta/recipes-devtools/ruby/ruby_2.2.5.bb b/meta/recipes-devtools/ruby/ruby_2.2.5.bb index 9238199fcf..bae1697cc4 100644 --- a/meta/recipes-devtools/ruby/ruby_2.2.5.bb +++ b/meta/recipes-devtools/ruby/ruby_2.2.5.bb @@ -3,7 +3,9 @@ require ruby.inc SRC_URI[md5sum] = "bd8e349d4fb2c75d90817649674f94be" SRC_URI[sha256sum] = "30c4b31697a4ca4ea0c8db8ad30cf45e6690a0f09687e5d483c933c03ca335e3" -SRC_URI += "file://prevent-gc.patch" +SRC_URI += "file://prevent-gc.patch \ + file://CVE-2016-7798.patch \ +" # it's unknown to configure script, but then passed to extconf.rb # maybe it's not really needed as we're hardcoding the result with -- cgit 1.2.3-korg