aboutsummaryrefslogtreecommitdiffstats
path: root/meta/recipes-devtools/ruby/ruby/CVE-2016-7798.patch
blob: 2b8772ba41ba579d1965d989d7d713d2b8475da3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
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 <trajaraman@mvista.com>

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