1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 Mikkel Krautz <mikkel@krautz.dk>
4 ** Copyright (C) 2016 Richard J. Moore <rich@kde.org>
5 ** Contact: https://www.qt.io/licensing/
6 **
7 ** This file is part of the QtNetwork module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** Commercial License Usage
11 ** Licensees holding valid commercial Qt licenses may use this file in
12 ** accordance with the commercial license agreement provided with the
13 ** Software or, alternatively, in accordance with the terms contained in
14 ** a written agreement between you and The Qt Company. For licensing terms
15 ** and conditions see https://www.qt.io/terms-conditions. For further
16 ** information use the contact form at https://www.qt.io/contact-us.
17 **
18 ** GNU Lesser General Public License Usage
19 ** Alternatively, this file may be used under the terms of the GNU Lesser
20 ** General Public License version 3 as published by the Free Software
21 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
22 ** packaging of this file. Please review the following information to
23 ** ensure the GNU Lesser General Public License version 3 requirements
24 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
25 **
26 ** GNU General Public License Usage
27 ** Alternatively, this file may be used under the terms of the GNU
28 ** General Public License version 2.0 or (at your option) the GNU General
29 ** Public license version 3 or any later version approved by the KDE Free
30 ** Qt Foundation. The licenses are as published by the Free Software
31 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
32 ** included in the packaging of this file. Please review the following
33 ** information to ensure the GNU General Public License requirements will
34 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
35 ** https://www.gnu.org/licenses/gpl-3.0.html.
36 **
37 ** $QT_END_LICENSE$
38 **
39 ****************************************************************************/
40 
41 #include "qssldiffiehellmanparameters.h"
42 #include "qssldiffiehellmanparameters_p.h"
43 #include "qsslsocket_openssl_symbols_p.h"
44 #include "qsslsocket.h"
45 #include "qsslsocket_p.h"
46 
47 #include "private/qssl_p.h"
48 
49 #include <QtCore/qatomic.h>
50 #include <QtCore/qbytearray.h>
51 #include <QtCore/qiodevice.h>
52 #include <QtCore/qscopeguard.h>
53 #ifndef QT_NO_DEBUG_STREAM
54 #include <QtCore/qdebug.h>
55 #endif
56 
57 #include <openssl/bn.h>
58 #include <openssl/dh.h>
59 
60 QT_BEGIN_NAMESPACE
61 
62 #ifdef OPENSSL_NO_DEPRECATED_3_0
63 
q_DH_check(DH * dh,int * status)64 static int q_DH_check(DH *dh, int *status)
65 {
66     // DH_check was first deprecated in OpenSSL 3.0.0, as low-level
67     // API; the EVP_PKEY family of functions was advised as an alternative.
68     // As of now EVP_PKEY_params_check ends up calling ... DH_check,
69     // which is good enough.
70 
71     Q_ASSERT(dh);
72     Q_ASSERT(status);
73 
74     EVP_PKEY *key = q_EVP_PKEY_new();
75     if (!key) {
76         qCWarning(lcSsl, "EVP_PKEY_new failed");
77         QSslSocketBackendPrivate::logAndClearErrorQueue();
78         return 0;
79     }
80     const auto keyDeleter = qScopeGuard([key](){
81         q_EVP_PKEY_free(key);
82     });
83     if (!q_EVP_PKEY_set1_DH(key, dh)) {
84         qCWarning(lcSsl, "EVP_PKEY_set1_DH failed");
85         QSslSocketBackendPrivate::logAndClearErrorQueue();
86         return 0;
87     }
88 
89     EVP_PKEY_CTX *keyCtx = q_EVP_PKEY_CTX_new(key, nullptr);
90     if (!keyCtx) {
91         qCWarning(lcSsl, "EVP_PKEY_CTX_new failed");
92         QSslSocketBackendPrivate::logAndClearErrorQueue();
93         return 0;
94     }
95     const auto ctxDeleter = qScopeGuard([keyCtx]{
96         q_EVP_PKEY_CTX_free(keyCtx);
97     });
98 
99     const int result = q_EVP_PKEY_param_check(keyCtx);
100     QSslSocketBackendPrivate::logAndClearErrorQueue();
101     // Note: unlike DH_check, we cannot obtain the 'status',
102     // if the 'result' is 0 (actually the result is 1 only
103     // if this 'status' was 0). We could probably check the
104     // errors from the error queue, but it's not needed anyway
105     // - see the 'isSafeDH' below, how it returns immediately
106     // on 0.
107     Q_UNUSED(status)
108 
109     return result;
110 }
111 #endif // OPENSSL_NO_DEPRECATED_3_0
112 
isSafeDH(DH * dh)113 static bool isSafeDH(DH *dh)
114 {
115     int status = 0;
116     int bad = 0;
117 
118     QSslSocketPrivate::ensureInitialized();
119 
120 
121     // From https://wiki.openssl.org/index.php/Diffie-Hellman_parameters:
122     //
123     //     The additional call to BN_mod_word(dh->p, 24)
124     //     (and unmasking of DH_NOT_SUITABLE_GENERATOR)
125     //     is performed to ensure your program accepts
126     //     IETF group parameters. OpenSSL checks the prime
127     //     is congruent to 11 when g = 2; while the IETF's
128     //     primes are congruent to 23 when g = 2.
129     //     Without the test, the IETF parameters would
130     //     fail validation. For details, see Diffie-Hellman
131     //     Parameter Check (when g = 2, must p mod 24 == 11?).
132     // Mark p < 1024 bits as unsafe.
133     if (q_DH_bits(dh) < 1024)
134         return false;
135 
136     if (q_DH_check(dh, &status) != 1)
137         return false;
138 
139     const BIGNUM *p = nullptr;
140     const BIGNUM *q = nullptr;
141     const BIGNUM *g = nullptr;
142     q_DH_get0_pqg(dh, &p, &q, &g);
143 
144     if (q_BN_is_word(const_cast<BIGNUM *>(g), DH_GENERATOR_2)) {
145         const unsigned long residue = q_BN_mod_word(p, 24);
146         if (residue == 11 || residue == 23)
147             status &= ~DH_NOT_SUITABLE_GENERATOR;
148     }
149 
150     bad |= DH_CHECK_P_NOT_PRIME;
151     bad |= DH_CHECK_P_NOT_SAFE_PRIME;
152     bad |= DH_NOT_SUITABLE_GENERATOR;
153 
154     return !(status & bad);
155 }
156 
decodeDer(const QByteArray & der)157 void QSslDiffieHellmanParametersPrivate::decodeDer(const QByteArray &der)
158 {
159     if (der.isEmpty()) {
160         error = QSslDiffieHellmanParameters::InvalidInputDataError;
161         return;
162     }
163 
164     const unsigned char *data = reinterpret_cast<const unsigned char *>(der.data());
165     int len = der.size();
166 
167     QSslSocketPrivate::ensureInitialized();
168 
169     DH *dh = q_d2i_DHparams(nullptr, &data, len);
170     if (dh) {
171         if (isSafeDH(dh))
172             derData = der;
173         else
174             error =  QSslDiffieHellmanParameters::UnsafeParametersError;
175     } else {
176         error = QSslDiffieHellmanParameters::InvalidInputDataError;
177     }
178 
179     q_DH_free(dh);
180 }
181 
decodePem(const QByteArray & pem)182 void QSslDiffieHellmanParametersPrivate::decodePem(const QByteArray &pem)
183 {
184     if (pem.isEmpty()) {
185         error = QSslDiffieHellmanParameters::InvalidInputDataError;
186         return;
187     }
188 
189     if (!QSslSocket::supportsSsl()) {
190         error = QSslDiffieHellmanParameters::InvalidInputDataError;
191         return;
192     }
193 
194     QSslSocketPrivate::ensureInitialized();
195 
196     BIO *bio = q_BIO_new_mem_buf(const_cast<char *>(pem.data()), pem.size());
197     if (!bio) {
198         error = QSslDiffieHellmanParameters::InvalidInputDataError;
199         return;
200     }
201 
202     DH *dh = nullptr;
203     q_PEM_read_bio_DHparams(bio, &dh, nullptr, nullptr);
204 
205     if (dh) {
206         if (isSafeDH(dh)) {
207             char *buf = nullptr;
208             int len = q_i2d_DHparams(dh, reinterpret_cast<unsigned char **>(&buf));
209             if (len > 0)
210                 derData = QByteArray(buf, len);
211             else
212                 error = QSslDiffieHellmanParameters::InvalidInputDataError;
213         } else {
214             error = QSslDiffieHellmanParameters::UnsafeParametersError;
215         }
216     } else {
217         error = QSslDiffieHellmanParameters::InvalidInputDataError;
218     }
219 
220     q_DH_free(dh);
221     q_BIO_free(bio);
222 }
223 
224 QT_END_NAMESPACE
225