aboutsummaryrefslogtreecommitdiffstats
path: root/meta-networking/recipes-daemons/proftpd/files/CVE-2016-3125.patch
blob: 69c9be031a82036d5fb12a6f50c799b09e667c2d (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
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
From 7a8f683cedf9b0d1024a80362693c9f8b93a0f2b Mon Sep 17 00:00:00 2001
From: TJ Saunders <tj@castaglia.org>
Date: Thu, 10 Mar 2016 15:07:58 -0800
Subject: [PATCH] Backport of fix for Bug#4230 to 1.3.5 branch.

Upstream-Status: Backport
CVE: CVE-2016-3125

Author: TJ Saunders <tj@castaglia.org>
Signed-off-by: Catalin Enache <catalin.enache@windriver.com>
---
 contrib/mod_tls.c | 167 +++++++++++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 147 insertions(+), 20 deletions(-)

diff --git a/contrib/mod_tls.c b/contrib/mod_tls.c
index df92658..5883cc7 100644
--- a/contrib/mod_tls.c
+++ b/contrib/mod_tls.c
@@ -411,6 +411,13 @@ static int tls_required_on_ctrl = 0;
 static int tls_required_on_data = 0;
 static unsigned char *tls_authenticated = NULL;
 
+/* Define the minimum DH group length we allow (unless the AllowWeakDH
+ * TLSOption is used).  Ideally this would be 2048, per https://weakdh.org,
+ * but for compatibility with older Java versions, which only support up to
+ * 1024, we'll use 1024.  For now.
+ */
+#define TLS_DH_MIN_LEN				1024
+
 /* mod_tls session flags */
 #define	TLS_SESS_ON_CTRL			0x0001
 #define TLS_SESS_ON_DATA			0x0002
@@ -438,6 +445,7 @@ static unsigned char *tls_authenticated = NULL;
 #define TLS_OPT_USE_IMPLICIT_SSL			0x0200
 #define TLS_OPT_ALLOW_CLIENT_RENEGOTIATIONS		0x0400
 #define TLS_OPT_VERIFY_CERT_CN				0x0800
+#define TLS_OPT_ALLOW_WEAK_DH				0x1000
 
 /* mod_tls SSCN modes */
 #define TLS_SSCN_MODE_SERVER				0
@@ -2417,24 +2425,139 @@ static int tls_ctrl_renegotiate_cb(CALLBACK_FRAME) {
 
 static DH *tls_dh_cb(SSL *ssl, int is_export, int keylength) {
   DH *dh = NULL;
+  EVP_PKEY *pkey;
+  int pkeylen = 0, use_pkeylen = FALSE;
+
+  /* OpenSSL will only ever call us (currently) with a keylen of 512 or 1024;
+   * see the SSL_EXPORT_PKEYLENGTH macro in ssl_locl.h.  Sigh.
+   *
+   * Thus we adjust the DH parameter length according to the size of the
+   * RSA/DSA private key used for the current connection.
+   *
+   * NOTE: This MAY cause interoperability issues with some clients, notably
+   * Java 7 (and earlier) clients, since Java 7 and earlier supports
+   * Diffie-Hellman only up to 1024 bits.  More sighs.  To deal with these
+   * clients, then, you need to configure a certificate/key of 1024 bits.
+   */
+  pkey = SSL_get_privatekey(ssl);
+  if (pkey != NULL) {
+    if (EVP_PKEY_type(pkey->type) == EVP_PKEY_RSA ||
+        EVP_PKEY_type(pkey->type) == EVP_PKEY_DSA) {
+      pkeylen = EVP_PKEY_bits(pkey);
+
+      if (pkeylen < TLS_DH_MIN_LEN) {
+        if (!(tls_opts & TLS_OPT_ALLOW_WEAK_DH)) {
+          pr_trace_msg(trace_channel, 11,
+            "certificate private key length %d less than %d bits, using %d "
+            "(see AllowWeakDH TLSOption)", pkeylen, TLS_DH_MIN_LEN,
+            TLS_DH_MIN_LEN);
+          pkeylen = TLS_DH_MIN_LEN;
+        }
+      }
+
+      if (pkeylen != keylen) {
+        pr_trace_msg(trace_channel, 13,
+          "adjusted DH parameter length from %d to %d bits", keylen, pkeylen);
+        use_pkeylen = TRUE;
+      }
+    }
+  }
 
   if (tls_tmp_dhs != NULL &&
       tls_tmp_dhs->nelts > 0) {
     register unsigned int i;
-    DH **dhs;
+    DH *best_dh = NULL, **dhs;
+    int best_dhlen = 0;
 
     dhs = tls_tmp_dhs->elts;
+
+    /* Search the configured list of DH parameters twice: once for any sizes
+     * matching the actual requested size (usually 1024), and once for any
+     * matching the certificate private key size (pkeylen).
+     *
+     * This behavior allows site admins to configure a TLSDHParamFile that
+     * contains 1024-bit parameters, for e.g. Java 7 (and earlier) clients.
+     */
+
+    /* Note: the keylen argument is in BITS, but DH_size() returns the number
+     * of BYTES.
+     */
     for (i = 0; i < tls_tmp_dhs->nelts; i++) {
-      /* Note: the keylength argument is in BITS, but DH_size() returns
-       * the number of BYTES.
+      int dhlen;
+
+      dhlen = DH_size(dhs[i]) * 8;
+      if (dhlen == keylen) {
+        pr_trace_msg(trace_channel, 11,
+          "found matching DH parameter for key length %d", keylen);
+        return dhs[i];
+      }
+
+      /* Try to find the next "best" DH to use, where "best" means
+       * the smallest DH that is larger than the necessary keylen.
        */
-      if (DH_size(dhs[i]) == (keylength / 8)) {
+      if (dhlen > keylen) {
+        if (best_dh != NULL) {
+          if (dhlen < best_dhlen) {
+            best_dh = dhs[i];
+            best_dhlen = dhlen;
+          }
+
+        } else {
+          best_dh = dhs[i];
+          best_dhlen = dhlen;
+        }
+      }
+    }
+
+    for (i = 0; i < tls_tmp_dhs->nelts; i++) {
+      int dhlen;
+
+      dhlen = DH_size(dhs[i]) * 8;
+      if (dhlen == pkeylen) {
+        pr_trace_msg(trace_channel, 11,
+          "found matching DH parameter for certificate private key length %d",
+          pkeylen);
         return dhs[i];
       }
+
+      if (dhlen > pkeylen) {
+        if (best_dh != NULL) {
+          if (dhlen < best_dhlen) {
+            best_dh = dhs[i];
+            best_dhlen = dhlen;
+          }
+
+        } else {
+          best_dh = dhs[i];
+          best_dhlen = dhlen;
+        }
+      }
+    }
+
+    if (best_dh != NULL) {
+      pr_trace_msg(trace_channel, 11,
+        "using best DH parameter for key length %d (length %d)", keylen,
+        best_dhlen);
+      return best_dh;
     }
   }
 
-  switch (keylength) {
+  /* Still no DH parameters found?  Use the built-in ones. */
+
+  if (keylen < TLS_DH_MIN_LEN) {
+    if (!(tls_opts & TLS_OPT_ALLOW_WEAK_DH)) {
+      pr_trace_msg(trace_channel, 11,
+        "requested key length %d less than %d bits, using %d "
+        "(see AllowWeakDH TLSOption)", keylen, TLS_DH_MIN_LEN, TLS_DH_MIN_LEN);
+      keylen = TLS_DH_MIN_LEN;
+    }
+  }
+
+  if (use_pkeylen) {
+    keylen = pkeylen;
+  }
+
+  switch (keylen) {
     case 512:
       dh = get_dh512();
       break;
@@ -2443,32 +2566,33 @@ static DH *tls_dh_cb(SSL *ssl, int is_export, int keylength) {
       dh = get_dh768();
       break;
 
-     case 1024:
-       dh = get_dh1024();
-       break;
+    case 1024:
+      dh = get_dh1024();
+      break;
 
-     case 1536:
-       dh = get_dh1536();
-       break;
+    case 1536:
+      dh = get_dh1536();
+      break;
 
-     case 2048:
-       dh = get_dh2048();
-       break;
+    case 2048:
+      dh = get_dh2048();
+      break;
 
-     default:
-       tls_log("unsupported DH key length %d requested, returning 1024 bits",
-         keylength);
-       dh = get_dh1024();
-       break;
+    default:
+      tls_log("unsupported DH key length %d requested, returning 1024 bits",
+        keylen);
+      dh = get_dh1024();
+      break;
   }
 
+  pr_trace_msg(trace_channel, 11, "using builtin DH for %d bits", keylen);
+
   /* Add this DH to the list, so that it can be freed properly later. */
   if (tls_tmp_dhs == NULL) {
     tls_tmp_dhs = make_array(session.pool, 1, sizeof(DH *));
   }
 
   *((DH **) push_array(tls_tmp_dhs)) = dh;
-
   return dh;
 }
 
@@ -8445,6 +8569,9 @@ MODRET set_tlsoptions(cmd_rec *cmd) {
                strcmp(cmd->argv[i], "AllowClientRenegotiations") == 0) {
       opts |= TLS_OPT_ALLOW_CLIENT_RENEGOTIATIONS;
 
+    } else if (strcmp(cmd->argv[i], "AllowWeakDH") == 0) {
+      opts |= TLS_OPT_ALLOW_WEAK_DH;
+
     } else if (strcmp(cmd->argv[i], "EnableDiags") == 0) {
       opts |= TLS_OPT_ENABLE_DIAGS;
 
-- 
2.7.4