Patrick Williams | b48b7b4 | 2016-08-17 15:04:38 -0500 | [diff] [blame^] | 1 | From 7a8f683cedf9b0d1024a80362693c9f8b93a0f2b Mon Sep 17 00:00:00 2001 |
| 2 | From: TJ Saunders <tj@castaglia.org> |
| 3 | Date: Thu, 10 Mar 2016 15:07:58 -0800 |
| 4 | Subject: [PATCH] Backport of fix for Bug#4230 to 1.3.5 branch. |
| 5 | |
| 6 | Upstream-Status: Backport |
| 7 | CVE: CVE-2016-3125 |
| 8 | |
| 9 | Author: TJ Saunders <tj@castaglia.org> |
| 10 | Signed-off-by: Catalin Enache <catalin.enache@windriver.com> |
| 11 | --- |
| 12 | contrib/mod_tls.c | 167 +++++++++++++++++++++++++++++++++++++++++++++++------- |
| 13 | 1 file changed, 147 insertions(+), 20 deletions(-) |
| 14 | |
| 15 | diff --git a/contrib/mod_tls.c b/contrib/mod_tls.c |
| 16 | index df92658..5883cc7 100644 |
| 17 | --- a/contrib/mod_tls.c |
| 18 | +++ b/contrib/mod_tls.c |
| 19 | @@ -411,6 +411,13 @@ static int tls_required_on_ctrl = 0; |
| 20 | static int tls_required_on_data = 0; |
| 21 | static unsigned char *tls_authenticated = NULL; |
| 22 | |
| 23 | +/* Define the minimum DH group length we allow (unless the AllowWeakDH |
| 24 | + * TLSOption is used). Ideally this would be 2048, per https://weakdh.org, |
| 25 | + * but for compatibility with older Java versions, which only support up to |
| 26 | + * 1024, we'll use 1024. For now. |
| 27 | + */ |
| 28 | +#define TLS_DH_MIN_LEN 1024 |
| 29 | + |
| 30 | /* mod_tls session flags */ |
| 31 | #define TLS_SESS_ON_CTRL 0x0001 |
| 32 | #define TLS_SESS_ON_DATA 0x0002 |
| 33 | @@ -438,6 +445,7 @@ static unsigned char *tls_authenticated = NULL; |
| 34 | #define TLS_OPT_USE_IMPLICIT_SSL 0x0200 |
| 35 | #define TLS_OPT_ALLOW_CLIENT_RENEGOTIATIONS 0x0400 |
| 36 | #define TLS_OPT_VERIFY_CERT_CN 0x0800 |
| 37 | +#define TLS_OPT_ALLOW_WEAK_DH 0x1000 |
| 38 | |
| 39 | /* mod_tls SSCN modes */ |
| 40 | #define TLS_SSCN_MODE_SERVER 0 |
| 41 | @@ -2417,24 +2425,139 @@ static int tls_ctrl_renegotiate_cb(CALLBACK_FRAME) { |
| 42 | |
| 43 | static DH *tls_dh_cb(SSL *ssl, int is_export, int keylength) { |
| 44 | DH *dh = NULL; |
| 45 | + EVP_PKEY *pkey; |
| 46 | + int pkeylen = 0, use_pkeylen = FALSE; |
| 47 | + |
| 48 | + /* OpenSSL will only ever call us (currently) with a keylen of 512 or 1024; |
| 49 | + * see the SSL_EXPORT_PKEYLENGTH macro in ssl_locl.h. Sigh. |
| 50 | + * |
| 51 | + * Thus we adjust the DH parameter length according to the size of the |
| 52 | + * RSA/DSA private key used for the current connection. |
| 53 | + * |
| 54 | + * NOTE: This MAY cause interoperability issues with some clients, notably |
| 55 | + * Java 7 (and earlier) clients, since Java 7 and earlier supports |
| 56 | + * Diffie-Hellman only up to 1024 bits. More sighs. To deal with these |
| 57 | + * clients, then, you need to configure a certificate/key of 1024 bits. |
| 58 | + */ |
| 59 | + pkey = SSL_get_privatekey(ssl); |
| 60 | + if (pkey != NULL) { |
| 61 | + if (EVP_PKEY_type(pkey->type) == EVP_PKEY_RSA || |
| 62 | + EVP_PKEY_type(pkey->type) == EVP_PKEY_DSA) { |
| 63 | + pkeylen = EVP_PKEY_bits(pkey); |
| 64 | + |
| 65 | + if (pkeylen < TLS_DH_MIN_LEN) { |
| 66 | + if (!(tls_opts & TLS_OPT_ALLOW_WEAK_DH)) { |
| 67 | + pr_trace_msg(trace_channel, 11, |
| 68 | + "certificate private key length %d less than %d bits, using %d " |
| 69 | + "(see AllowWeakDH TLSOption)", pkeylen, TLS_DH_MIN_LEN, |
| 70 | + TLS_DH_MIN_LEN); |
| 71 | + pkeylen = TLS_DH_MIN_LEN; |
| 72 | + } |
| 73 | + } |
| 74 | + |
| 75 | + if (pkeylen != keylen) { |
| 76 | + pr_trace_msg(trace_channel, 13, |
| 77 | + "adjusted DH parameter length from %d to %d bits", keylen, pkeylen); |
| 78 | + use_pkeylen = TRUE; |
| 79 | + } |
| 80 | + } |
| 81 | + } |
| 82 | |
| 83 | if (tls_tmp_dhs != NULL && |
| 84 | tls_tmp_dhs->nelts > 0) { |
| 85 | register unsigned int i; |
| 86 | - DH **dhs; |
| 87 | + DH *best_dh = NULL, **dhs; |
| 88 | + int best_dhlen = 0; |
| 89 | |
| 90 | dhs = tls_tmp_dhs->elts; |
| 91 | + |
| 92 | + /* Search the configured list of DH parameters twice: once for any sizes |
| 93 | + * matching the actual requested size (usually 1024), and once for any |
| 94 | + * matching the certificate private key size (pkeylen). |
| 95 | + * |
| 96 | + * This behavior allows site admins to configure a TLSDHParamFile that |
| 97 | + * contains 1024-bit parameters, for e.g. Java 7 (and earlier) clients. |
| 98 | + */ |
| 99 | + |
| 100 | + /* Note: the keylen argument is in BITS, but DH_size() returns the number |
| 101 | + * of BYTES. |
| 102 | + */ |
| 103 | for (i = 0; i < tls_tmp_dhs->nelts; i++) { |
| 104 | - /* Note: the keylength argument is in BITS, but DH_size() returns |
| 105 | - * the number of BYTES. |
| 106 | + int dhlen; |
| 107 | + |
| 108 | + dhlen = DH_size(dhs[i]) * 8; |
| 109 | + if (dhlen == keylen) { |
| 110 | + pr_trace_msg(trace_channel, 11, |
| 111 | + "found matching DH parameter for key length %d", keylen); |
| 112 | + return dhs[i]; |
| 113 | + } |
| 114 | + |
| 115 | + /* Try to find the next "best" DH to use, where "best" means |
| 116 | + * the smallest DH that is larger than the necessary keylen. |
| 117 | */ |
| 118 | - if (DH_size(dhs[i]) == (keylength / 8)) { |
| 119 | + if (dhlen > keylen) { |
| 120 | + if (best_dh != NULL) { |
| 121 | + if (dhlen < best_dhlen) { |
| 122 | + best_dh = dhs[i]; |
| 123 | + best_dhlen = dhlen; |
| 124 | + } |
| 125 | + |
| 126 | + } else { |
| 127 | + best_dh = dhs[i]; |
| 128 | + best_dhlen = dhlen; |
| 129 | + } |
| 130 | + } |
| 131 | + } |
| 132 | + |
| 133 | + for (i = 0; i < tls_tmp_dhs->nelts; i++) { |
| 134 | + int dhlen; |
| 135 | + |
| 136 | + dhlen = DH_size(dhs[i]) * 8; |
| 137 | + if (dhlen == pkeylen) { |
| 138 | + pr_trace_msg(trace_channel, 11, |
| 139 | + "found matching DH parameter for certificate private key length %d", |
| 140 | + pkeylen); |
| 141 | return dhs[i]; |
| 142 | } |
| 143 | + |
| 144 | + if (dhlen > pkeylen) { |
| 145 | + if (best_dh != NULL) { |
| 146 | + if (dhlen < best_dhlen) { |
| 147 | + best_dh = dhs[i]; |
| 148 | + best_dhlen = dhlen; |
| 149 | + } |
| 150 | + |
| 151 | + } else { |
| 152 | + best_dh = dhs[i]; |
| 153 | + best_dhlen = dhlen; |
| 154 | + } |
| 155 | + } |
| 156 | + } |
| 157 | + |
| 158 | + if (best_dh != NULL) { |
| 159 | + pr_trace_msg(trace_channel, 11, |
| 160 | + "using best DH parameter for key length %d (length %d)", keylen, |
| 161 | + best_dhlen); |
| 162 | + return best_dh; |
| 163 | } |
| 164 | } |
| 165 | |
| 166 | - switch (keylength) { |
| 167 | + /* Still no DH parameters found? Use the built-in ones. */ |
| 168 | + |
| 169 | + if (keylen < TLS_DH_MIN_LEN) { |
| 170 | + if (!(tls_opts & TLS_OPT_ALLOW_WEAK_DH)) { |
| 171 | + pr_trace_msg(trace_channel, 11, |
| 172 | + "requested key length %d less than %d bits, using %d " |
| 173 | + "(see AllowWeakDH TLSOption)", keylen, TLS_DH_MIN_LEN, TLS_DH_MIN_LEN); |
| 174 | + keylen = TLS_DH_MIN_LEN; |
| 175 | + } |
| 176 | + } |
| 177 | + |
| 178 | + if (use_pkeylen) { |
| 179 | + keylen = pkeylen; |
| 180 | + } |
| 181 | + |
| 182 | + switch (keylen) { |
| 183 | case 512: |
| 184 | dh = get_dh512(); |
| 185 | break; |
| 186 | @@ -2443,32 +2566,33 @@ static DH *tls_dh_cb(SSL *ssl, int is_export, int keylength) { |
| 187 | dh = get_dh768(); |
| 188 | break; |
| 189 | |
| 190 | - case 1024: |
| 191 | - dh = get_dh1024(); |
| 192 | - break; |
| 193 | + case 1024: |
| 194 | + dh = get_dh1024(); |
| 195 | + break; |
| 196 | |
| 197 | - case 1536: |
| 198 | - dh = get_dh1536(); |
| 199 | - break; |
| 200 | + case 1536: |
| 201 | + dh = get_dh1536(); |
| 202 | + break; |
| 203 | |
| 204 | - case 2048: |
| 205 | - dh = get_dh2048(); |
| 206 | - break; |
| 207 | + case 2048: |
| 208 | + dh = get_dh2048(); |
| 209 | + break; |
| 210 | |
| 211 | - default: |
| 212 | - tls_log("unsupported DH key length %d requested, returning 1024 bits", |
| 213 | - keylength); |
| 214 | - dh = get_dh1024(); |
| 215 | - break; |
| 216 | + default: |
| 217 | + tls_log("unsupported DH key length %d requested, returning 1024 bits", |
| 218 | + keylen); |
| 219 | + dh = get_dh1024(); |
| 220 | + break; |
| 221 | } |
| 222 | |
| 223 | + pr_trace_msg(trace_channel, 11, "using builtin DH for %d bits", keylen); |
| 224 | + |
| 225 | /* Add this DH to the list, so that it can be freed properly later. */ |
| 226 | if (tls_tmp_dhs == NULL) { |
| 227 | tls_tmp_dhs = make_array(session.pool, 1, sizeof(DH *)); |
| 228 | } |
| 229 | |
| 230 | *((DH **) push_array(tls_tmp_dhs)) = dh; |
| 231 | - |
| 232 | return dh; |
| 233 | } |
| 234 | |
| 235 | @@ -8445,6 +8569,9 @@ MODRET set_tlsoptions(cmd_rec *cmd) { |
| 236 | strcmp(cmd->argv[i], "AllowClientRenegotiations") == 0) { |
| 237 | opts |= TLS_OPT_ALLOW_CLIENT_RENEGOTIATIONS; |
| 238 | |
| 239 | + } else if (strcmp(cmd->argv[i], "AllowWeakDH") == 0) { |
| 240 | + opts |= TLS_OPT_ALLOW_WEAK_DH; |
| 241 | + |
| 242 | } else if (strcmp(cmd->argv[i], "EnableDiags") == 0) { |
| 243 | opts |= TLS_OPT_ENABLE_DIAGS; |
| 244 | |
| 245 | -- |
| 246 | 2.7.4 |
| 247 | |