1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2017 The Qt Company Ltd. |
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 "qssl_p.h" |
42 | #include "qsslsocket_openssl_symbols_p.h" |
43 | #include "qsslcertificate_p.h" |
44 | #include "qsslkey_p.h" |
45 | #include "qsslcertificateextension_p.h" |
46 | |
47 | #include <QtCore/qscopeguard.h> |
48 | #include <QtCore/qendian.h> |
49 | #include <QtCore/qmutex.h> |
50 | |
51 | QT_BEGIN_NAMESPACE |
52 | |
53 | Q_CONSTEXPR int MutexPoolSize = 17; |
54 | static QBasicMutex mutexPool[MutexPoolSize]; |
55 | namespace QMutexPool { |
56 | static QBasicMutex *globalInstanceGet(const void *addr) |
57 | { |
58 | return mutexPool + (quintptr(addr) % MutexPoolSize); |
59 | } |
60 | } |
61 | |
62 | // forward declaration |
63 | static QMultiMap<QByteArray, QString> _q_mapFromX509Name(X509_NAME *name); |
64 | |
65 | bool QSslCertificate::operator==(const QSslCertificate &other) const |
66 | { |
67 | if (d == other.d) |
68 | return true; |
69 | |
70 | if (d->null && other.d->null) |
71 | return true; |
72 | |
73 | if (d->x509 && other.d->x509) { |
74 | const int ret = q_X509_cmp(a: d->x509, b: other.d->x509); |
75 | if (ret >= -1 && ret <= 1) |
76 | return ret == 0; |
77 | QSslSocketBackendPrivate::logAndClearErrorQueue(); |
78 | } |
79 | |
80 | return false; |
81 | } |
82 | |
83 | uint qHash(const QSslCertificate &key, uint seed) noexcept |
84 | { |
85 | if (X509 * const x509 = key.d->x509) { |
86 | const EVP_MD *sha1 = q_EVP_sha1(); |
87 | unsigned int len = 0; |
88 | unsigned char md[EVP_MAX_MD_SIZE]; |
89 | q_X509_digest(x509, type: sha1, md, len: &len); |
90 | return qHashBits(p: md, size: len, seed); |
91 | } |
92 | |
93 | return seed; |
94 | } |
95 | |
96 | bool QSslCertificate::isNull() const |
97 | { |
98 | return d->null; |
99 | } |
100 | |
101 | bool QSslCertificate::isSelfSigned() const |
102 | { |
103 | if (!d->x509) |
104 | return false; |
105 | |
106 | return (q_X509_check_issued(a: d->x509, b: d->x509) == X509_V_OK); |
107 | } |
108 | |
109 | QByteArray QSslCertificate::version() const |
110 | { |
111 | #if QT_CONFIG(thread) |
112 | QMutexLocker lock(QMutexPool::globalInstanceGet(addr: d.data())); |
113 | #endif |
114 | if (d->versionString.isEmpty() && d->x509) |
115 | d->versionString = QByteArray::number(qlonglong(q_X509_get_version(a: d->x509)) + 1); |
116 | |
117 | return d->versionString; |
118 | } |
119 | |
120 | QByteArray QSslCertificate::serialNumber() const |
121 | { |
122 | #if QT_CONFIG(thread) |
123 | QMutexLocker lock(QMutexPool::globalInstanceGet(addr: d.data())); |
124 | #endif |
125 | if (d->serialNumberString.isEmpty() && d->x509) { |
126 | ASN1_INTEGER *serialNumber = q_X509_get_serialNumber(a: d->x509); |
127 | QByteArray hexString; |
128 | hexString.reserve(asize: serialNumber->length * 3); |
129 | for (int a = 0; a < serialNumber->length; ++a) { |
130 | hexString += QByteArray::number(serialNumber->data[a], base: 16).rightJustified(width: 2, fill: '0'); |
131 | hexString += ':'; |
132 | } |
133 | hexString.chop(n: 1); |
134 | d->serialNumberString = hexString; |
135 | } |
136 | return d->serialNumberString; |
137 | } |
138 | |
139 | QStringList QSslCertificate::issuerInfo(SubjectInfo info) const |
140 | { |
141 | #if QT_CONFIG(thread) |
142 | QMutexLocker lock(QMutexPool::globalInstanceGet(addr: d.data())); |
143 | #endif |
144 | // lazy init |
145 | if (d->issuerInfo.isEmpty() && d->x509) |
146 | d->issuerInfo = |
147 | _q_mapFromX509Name(name: q_X509_get_issuer_name(a: d->x509)); |
148 | |
149 | return d->issuerInfo.values(akey: d->subjectInfoToString(info)); |
150 | } |
151 | |
152 | QStringList QSslCertificate::issuerInfo(const QByteArray &attribute) const |
153 | { |
154 | #if QT_CONFIG(thread) |
155 | QMutexLocker lock(QMutexPool::globalInstanceGet(addr: d.data())); |
156 | #endif |
157 | // lazy init |
158 | if (d->issuerInfo.isEmpty() && d->x509) |
159 | d->issuerInfo = |
160 | _q_mapFromX509Name(name: q_X509_get_issuer_name(a: d->x509)); |
161 | |
162 | return d->issuerInfo.values(akey: attribute); |
163 | } |
164 | |
165 | QStringList QSslCertificate::subjectInfo(SubjectInfo info) const |
166 | { |
167 | #if QT_CONFIG(thread) |
168 | QMutexLocker lock(QMutexPool::globalInstanceGet(addr: d.data())); |
169 | #endif |
170 | // lazy init |
171 | if (d->subjectInfo.isEmpty() && d->x509) |
172 | d->subjectInfo = |
173 | _q_mapFromX509Name(name: q_X509_get_subject_name(a: d->x509)); |
174 | |
175 | return d->subjectInfo.values(akey: d->subjectInfoToString(info)); |
176 | } |
177 | |
178 | QStringList QSslCertificate::subjectInfo(const QByteArray &attribute) const |
179 | { |
180 | #if QT_CONFIG(thread) |
181 | QMutexLocker lock(QMutexPool::globalInstanceGet(addr: d.data())); |
182 | #endif |
183 | // lazy init |
184 | if (d->subjectInfo.isEmpty() && d->x509) |
185 | d->subjectInfo = |
186 | _q_mapFromX509Name(name: q_X509_get_subject_name(a: d->x509)); |
187 | |
188 | return d->subjectInfo.values(akey: attribute); |
189 | } |
190 | |
191 | QList<QByteArray> QSslCertificate::subjectInfoAttributes() const |
192 | { |
193 | #if QT_CONFIG(thread) |
194 | QMutexLocker lock(QMutexPool::globalInstanceGet(addr: d.data())); |
195 | #endif |
196 | // lazy init |
197 | if (d->subjectInfo.isEmpty() && d->x509) |
198 | d->subjectInfo = |
199 | _q_mapFromX509Name(name: q_X509_get_subject_name(a: d->x509)); |
200 | |
201 | return d->subjectInfo.uniqueKeys(); |
202 | } |
203 | |
204 | QList<QByteArray> QSslCertificate::issuerInfoAttributes() const |
205 | { |
206 | #if QT_CONFIG(thread) |
207 | QMutexLocker lock(QMutexPool::globalInstanceGet(addr: d.data())); |
208 | #endif |
209 | // lazy init |
210 | if (d->issuerInfo.isEmpty() && d->x509) |
211 | d->issuerInfo = |
212 | _q_mapFromX509Name(name: q_X509_get_issuer_name(a: d->x509)); |
213 | |
214 | return d->issuerInfo.uniqueKeys(); |
215 | } |
216 | |
217 | QMultiMap<QSsl::AlternativeNameEntryType, QString> QSslCertificate::subjectAlternativeNames() const |
218 | { |
219 | QMultiMap<QSsl::AlternativeNameEntryType, QString> result; |
220 | |
221 | if (!d->x509) |
222 | return result; |
223 | |
224 | STACK_OF(GENERAL_NAME) *altNames = (STACK_OF(GENERAL_NAME) *)q_X509_get_ext_d2i( |
225 | a: d->x509, NID_subject_alt_name, c: nullptr, d: nullptr); |
226 | |
227 | auto altName = [](ASN1_IA5STRING *ia5, int len) { |
228 | const char *altNameStr = reinterpret_cast<const char *>(q_ASN1_STRING_get0_data(x: ia5)); |
229 | return QString::fromLatin1(str: altNameStr, size: len); |
230 | }; |
231 | if (altNames) { |
232 | for (int i = 0; i < q_sk_GENERAL_NAME_num(altNames); ++i) { |
233 | const GENERAL_NAME *genName = q_sk_GENERAL_NAME_value(altNames, i); |
234 | if (genName->type != GEN_DNS && genName->type != GEN_EMAIL && genName->type != GEN_IPADD) |
235 | continue; |
236 | |
237 | int len = q_ASN1_STRING_length(a: genName->d.ia5); |
238 | if (len < 0 || len >= 8192) { |
239 | // broken name |
240 | continue; |
241 | } |
242 | |
243 | switch (genName->type) { |
244 | case GEN_DNS: |
245 | result.insert(akey: QSsl::DnsEntry, avalue: altName(genName->d.ia5, len)); |
246 | break; |
247 | case GEN_EMAIL: |
248 | result.insert(akey: QSsl::EmailEntry, avalue: altName(genName->d.ia5, len)); |
249 | break; |
250 | case GEN_IPADD: { |
251 | QHostAddress ipAddress; |
252 | switch (len) { |
253 | case 4: // IPv4 |
254 | ipAddress = QHostAddress(qFromBigEndian(source: *reinterpret_cast<quint32 *>(genName->d.iPAddress->data))); |
255 | break; |
256 | case 16: // IPv6 |
257 | ipAddress = QHostAddress(reinterpret_cast<quint8 *>(genName->d.iPAddress->data)); |
258 | break; |
259 | default: // Unknown IP address format |
260 | break; |
261 | } |
262 | if (!ipAddress.isNull()) |
263 | result.insert(akey: QSsl::IpAddressEntry, avalue: ipAddress.toString()); |
264 | break; |
265 | } |
266 | default: |
267 | break; |
268 | } |
269 | } |
270 | |
271 | q_OPENSSL_sk_pop_free(a: (OPENSSL_STACK*)altNames, b: reinterpret_cast<void(*)(void*)>(q_GENERAL_NAME_free)); |
272 | } |
273 | |
274 | return result; |
275 | } |
276 | |
277 | QDateTime QSslCertificate::effectiveDate() const |
278 | { |
279 | return d->notValidBefore; |
280 | } |
281 | |
282 | QDateTime QSslCertificate::expiryDate() const |
283 | { |
284 | return d->notValidAfter; |
285 | } |
286 | |
287 | Qt::HANDLE QSslCertificate::handle() const |
288 | { |
289 | return Qt::HANDLE(d->x509); |
290 | } |
291 | |
292 | QSslKey QSslCertificate::publicKey() const |
293 | { |
294 | if (!d->x509) |
295 | return QSslKey(); |
296 | |
297 | QSslKey key; |
298 | |
299 | key.d->type = QSsl::PublicKey; |
300 | |
301 | EVP_PKEY *pkey = q_X509_get_pubkey(a: d->x509); |
302 | Q_ASSERT(pkey); |
303 | const int keyType = q_EVP_PKEY_type(a: q_EVP_PKEY_base_id(a: pkey)); |
304 | |
305 | if (keyType == EVP_PKEY_RSA) { |
306 | key.d->rsa = q_EVP_PKEY_get1_RSA(a: pkey); |
307 | key.d->algorithm = QSsl::Rsa; |
308 | key.d->isNull = false; |
309 | } else if (keyType == EVP_PKEY_DSA) { |
310 | key.d->dsa = q_EVP_PKEY_get1_DSA(a: pkey); |
311 | key.d->algorithm = QSsl::Dsa; |
312 | key.d->isNull = false; |
313 | #ifndef OPENSSL_NO_EC |
314 | } else if (keyType == EVP_PKEY_EC) { |
315 | key.d->ec = q_EVP_PKEY_get1_EC_KEY(a: pkey); |
316 | key.d->algorithm = QSsl::Ec; |
317 | key.d->isNull = false; |
318 | #endif |
319 | } else if (keyType == EVP_PKEY_DH) { |
320 | // DH unsupported |
321 | } else { |
322 | // error? |
323 | } |
324 | |
325 | q_EVP_PKEY_free(a: pkey); |
326 | return key; |
327 | } |
328 | |
329 | /* |
330 | * Convert unknown extensions to a QVariant. |
331 | */ |
332 | static QVariant x509UnknownExtensionToValue(X509_EXTENSION *ext) |
333 | { |
334 | Q_ASSERT(ext); |
335 | // Get the extension specific method object if available, |
336 | // we cast away the const-ness here because some versions of openssl |
337 | // don't use const for the parameters in the functions pointers stored |
338 | // in the object. |
339 | X509V3_EXT_METHOD *meth = const_cast<X509V3_EXT_METHOD *>(q_X509V3_EXT_get(a: ext)); |
340 | if (!meth) { |
341 | ASN1_OCTET_STRING *value = q_X509_EXTENSION_get_data(a: ext); |
342 | Q_ASSERT(value); |
343 | QByteArray result( reinterpret_cast<const char *>(q_ASN1_STRING_get0_data(x: value)), |
344 | q_ASN1_STRING_length(a: value)); |
345 | return result; |
346 | } |
347 | |
348 | void *ext_internal = q_X509V3_EXT_d2i(a: ext); |
349 | if (!ext_internal) |
350 | return {}; |
351 | |
352 | const auto extCleaner = qScopeGuard(f: [meth, ext_internal]{ |
353 | Q_ASSERT(ext_internal && meth); |
354 | |
355 | if (meth->it) |
356 | q_ASN1_item_free(val: static_cast<ASN1_VALUE *>(ext_internal), ASN1_ITEM_ptr(meth->it)); |
357 | else if (meth->ext_free) |
358 | meth->ext_free(ext_internal); |
359 | else |
360 | qCWarning(lcSsl, "No method to free an unknown extension, a potential memory leak?" ); |
361 | }); |
362 | |
363 | // If this extension can be converted |
364 | if (meth->i2v) { |
365 | STACK_OF(CONF_VALUE) *val = meth->i2v(meth, ext_internal, nullptr); |
366 | const auto stackCleaner = qScopeGuard(f: [val]{ |
367 | if (val) |
368 | q_OPENSSL_sk_pop_free(a: (OPENSSL_STACK *)val, b: (void(*)(void*))q_X509V3_conf_free); |
369 | }); |
370 | |
371 | QVariantMap map; |
372 | QVariantList list; |
373 | bool isMap = false; |
374 | |
375 | for (int j = 0; j < q_SKM_sk_num(val); j++) { |
376 | CONF_VALUE *nval = q_SKM_sk_value(CONF_VALUE, val, j); |
377 | if (nval->name && nval->value) { |
378 | isMap = true; |
379 | map[QString::fromUtf8(str: nval->name)] = QString::fromUtf8(str: nval->value); |
380 | } else if (nval->name) { |
381 | list << QString::fromUtf8(str: nval->name); |
382 | } else if (nval->value) { |
383 | list << QString::fromUtf8(str: nval->value); |
384 | } |
385 | } |
386 | |
387 | if (isMap) |
388 | return map; |
389 | else |
390 | return list; |
391 | } else if (meth->i2s) { |
392 | const char *hexString = meth->i2s(meth, ext_internal); |
393 | QVariant result(hexString ? QString::fromUtf8(str: hexString) : QString{}); |
394 | q_OPENSSL_free((void *)hexString); |
395 | return result; |
396 | } else if (meth->i2r) { |
397 | QByteArray result; |
398 | |
399 | BIO *bio = q_BIO_new(a: q_BIO_s_mem()); |
400 | if (!bio) |
401 | return result; |
402 | |
403 | meth->i2r(meth, ext_internal, bio, 0); |
404 | |
405 | char *bio_buffer; |
406 | long bio_size = q_BIO_get_mem_data(bio, &bio_buffer); |
407 | result = QByteArray(bio_buffer, bio_size); |
408 | |
409 | q_BIO_free(a: bio); |
410 | return result; |
411 | } |
412 | |
413 | return QVariant(); |
414 | } |
415 | |
416 | /* |
417 | * Convert extensions to a variant. The naming of the keys of the map are |
418 | * taken from RFC 5280, however we decided the capitalisation in the RFC |
419 | * was too silly for the real world. |
420 | */ |
421 | static QVariant x509ExtensionToValue(X509_EXTENSION *ext) |
422 | { |
423 | ASN1_OBJECT *obj = q_X509_EXTENSION_get_object(a: ext); |
424 | int nid = q_OBJ_obj2nid(a: obj); |
425 | |
426 | // We cast away the const-ness here because some versions of openssl |
427 | // don't use const for the parameters in the functions pointers stored |
428 | // in the object. |
429 | X509V3_EXT_METHOD *meth = const_cast<X509V3_EXT_METHOD *>(q_X509V3_EXT_get(a: ext)); |
430 | |
431 | void *ext_internal = nullptr; // The value, returned by X509V3_EXT_d2i. |
432 | const auto extCleaner = qScopeGuard(f: [meth, &ext_internal]() { |
433 | if (!meth || !ext_internal) |
434 | return; |
435 | |
436 | if (meth->it) |
437 | q_ASN1_item_free(val: static_cast<ASN1_VALUE *>(ext_internal), ASN1_ITEM_ptr(meth->it)); |
438 | else if (meth->ext_free) |
439 | meth->ext_free(ext_internal); |
440 | else |
441 | qWarning(catFunc: lcSsl, msg: "Cannot free an extension, a potential memory leak?" ); |
442 | }); |
443 | |
444 | const char * hexString = nullptr; // The value returned by meth->i2s. |
445 | const auto hexStringCleaner = qScopeGuard(f: [&hexString](){ |
446 | if (hexString) |
447 | q_OPENSSL_free((void*)hexString); |
448 | }); |
449 | |
450 | switch (nid) { |
451 | case NID_basic_constraints: |
452 | { |
453 | BASIC_CONSTRAINTS *basic = reinterpret_cast<BASIC_CONSTRAINTS *>(q_X509V3_EXT_d2i(a: ext)); |
454 | if (!basic) |
455 | return {}; |
456 | QVariantMap result; |
457 | result[QLatin1String("ca" )] = basic->ca ? true : false; |
458 | if (basic->pathlen) |
459 | result[QLatin1String("pathLenConstraint" )] = (qlonglong)q_ASN1_INTEGER_get(a: basic->pathlen); |
460 | |
461 | q_BASIC_CONSTRAINTS_free(a: basic); |
462 | return result; |
463 | } |
464 | break; |
465 | case NID_info_access: |
466 | { |
467 | AUTHORITY_INFO_ACCESS *info = reinterpret_cast<AUTHORITY_INFO_ACCESS *>(q_X509V3_EXT_d2i(a: ext)); |
468 | if (!info) |
469 | return {}; |
470 | QVariantMap result; |
471 | for (int i=0; i < q_SKM_sk_num(info); i++) { |
472 | ACCESS_DESCRIPTION *ad = q_SKM_sk_value(ACCESS_DESCRIPTION, info, i); |
473 | |
474 | GENERAL_NAME *name = ad->location; |
475 | if (name->type == GEN_URI) { |
476 | int len = q_ASN1_STRING_length(a: name->d.uniformResourceIdentifier); |
477 | if (len < 0 || len >= 8192) { |
478 | // broken name |
479 | continue; |
480 | } |
481 | |
482 | const char *uriStr = reinterpret_cast<const char *>(q_ASN1_STRING_get0_data(x: name->d.uniformResourceIdentifier)); |
483 | const QString uri = QString::fromUtf8(str: uriStr, size: len); |
484 | |
485 | result[QString::fromUtf8(str: QSslCertificatePrivate::asn1ObjectName(object: ad->method))] = uri; |
486 | } else { |
487 | qCWarning(lcSsl) << "Strange location type" << name->type; |
488 | } |
489 | } |
490 | |
491 | q_AUTHORITY_INFO_ACCESS_free(a: info); |
492 | return result; |
493 | } |
494 | break; |
495 | case NID_subject_key_identifier: |
496 | { |
497 | ext_internal = q_X509V3_EXT_d2i(a: ext); |
498 | if (!ext_internal) |
499 | return {}; |
500 | |
501 | hexString = meth->i2s(meth, ext_internal); |
502 | return QVariant(QString::fromUtf8(str: hexString)); |
503 | } |
504 | break; |
505 | case NID_authority_key_identifier: |
506 | { |
507 | AUTHORITY_KEYID *auth_key = reinterpret_cast<AUTHORITY_KEYID *>(q_X509V3_EXT_d2i(a: ext)); |
508 | if (!auth_key) |
509 | return {}; |
510 | QVariantMap result; |
511 | |
512 | // keyid |
513 | if (auth_key->keyid) { |
514 | QByteArray keyid(reinterpret_cast<const char *>(auth_key->keyid->data), |
515 | auth_key->keyid->length); |
516 | result[QLatin1String("keyid" )] = keyid.toHex(); |
517 | } |
518 | |
519 | // issuer |
520 | // TODO: GENERAL_NAMES |
521 | |
522 | // serial |
523 | if (auth_key->serial) |
524 | result[QLatin1String("serial" )] = (qlonglong)q_ASN1_INTEGER_get(a: auth_key->serial); |
525 | |
526 | q_AUTHORITY_KEYID_free(a: auth_key); |
527 | return result; |
528 | } |
529 | break; |
530 | } |
531 | |
532 | return {}; |
533 | } |
534 | |
535 | QSslCertificateExtension QSslCertificatePrivate::convertExtension(X509_EXTENSION *ext) |
536 | { |
537 | Q_ASSERT(ext); |
538 | |
539 | QSslCertificateExtension result; |
540 | |
541 | ASN1_OBJECT *obj = q_X509_EXTENSION_get_object(a: ext); |
542 | if (!obj) { |
543 | qCWarning(lcSsl, "Invalid (nullptr) ASN1_OBJECT" ); |
544 | return result; |
545 | } |
546 | |
547 | QByteArray oid = QSslCertificatePrivate::asn1ObjectId(object: obj); |
548 | QByteArray name = QSslCertificatePrivate::asn1ObjectName(object: obj); |
549 | |
550 | result.d->oid = QString::fromUtf8(str: oid); |
551 | result.d->name = QString::fromUtf8(str: name); |
552 | |
553 | bool critical = q_X509_EXTENSION_get_critical(a: ext); |
554 | result.d->critical = critical; |
555 | |
556 | // Lets see if we have custom support for this one |
557 | QVariant extensionValue = x509ExtensionToValue(ext); |
558 | if (extensionValue.isValid()) { |
559 | result.d->value = extensionValue; |
560 | result.d->supported = true; |
561 | |
562 | return result; |
563 | } |
564 | |
565 | extensionValue = x509UnknownExtensionToValue(ext); |
566 | if (extensionValue.isValid()) { |
567 | result.d->value = extensionValue; |
568 | result.d->supported = false; |
569 | return result; |
570 | } |
571 | |
572 | return result; |
573 | } |
574 | |
575 | QList<QSslCertificateExtension> QSslCertificate::extensions() const |
576 | { |
577 | QList<QSslCertificateExtension> result; |
578 | |
579 | if (!d->x509) |
580 | return result; |
581 | |
582 | int count = q_X509_get_ext_count(a: d->x509); |
583 | if (count <= 0) |
584 | return result; |
585 | |
586 | result.reserve(alloc: count); |
587 | |
588 | for (int i = 0; i < count; i++) { |
589 | X509_EXTENSION *ext = q_X509_get_ext(a: d->x509, b: i); |
590 | if (!ext) { |
591 | qCWarning(lcSsl) << "Invalid (nullptr) extension at index" << i; |
592 | continue; |
593 | } |
594 | result << QSslCertificatePrivate::convertExtension(ext); |
595 | } |
596 | |
597 | // Converting an extension may result in an error(s), clean them up. |
598 | Q_UNUSED(QSslSocketBackendPrivate::getErrorsFromOpenSsl()); |
599 | |
600 | return result; |
601 | } |
602 | |
603 | QByteArray QSslCertificate::toPem() const |
604 | { |
605 | if (!d->x509) |
606 | return QByteArray(); |
607 | return d->QByteArray_from_X509(x509: d->x509, format: QSsl::Pem); |
608 | } |
609 | |
610 | QByteArray QSslCertificate::toDer() const |
611 | { |
612 | if (!d->x509) |
613 | return QByteArray(); |
614 | return d->QByteArray_from_X509(x509: d->x509, format: QSsl::Der); |
615 | } |
616 | |
617 | QString QSslCertificate::toText() const |
618 | { |
619 | if (!d->x509) |
620 | return QString(); |
621 | return d->text_from_X509(x509: d->x509); |
622 | } |
623 | |
624 | #define BEGINCERTSTRING "-----BEGIN CERTIFICATE-----" |
625 | #define ENDCERTSTRING "-----END CERTIFICATE-----" |
626 | |
627 | void QSslCertificatePrivate::init(const QByteArray &data, QSsl::EncodingFormat format) |
628 | { |
629 | if (!data.isEmpty()) { |
630 | const QList<QSslCertificate> certs = (format == QSsl::Pem) |
631 | ? certificatesFromPem(pem: data, count: 1) |
632 | : certificatesFromDer(der: data, count: 1); |
633 | if (!certs.isEmpty()) { |
634 | *this = *certs.first().d; |
635 | if (x509) |
636 | x509 = q_X509_dup(a: x509); |
637 | } |
638 | } |
639 | } |
640 | |
641 | // ### refactor against QSsl::pemFromDer() etc. (to avoid redundant implementations) |
642 | QByteArray QSslCertificatePrivate::QByteArray_from_X509(X509 *x509, QSsl::EncodingFormat format) |
643 | { |
644 | if (!x509) { |
645 | qCWarning(lcSsl, "QSslSocketBackendPrivate::X509_to_QByteArray: null X509" ); |
646 | return QByteArray(); |
647 | } |
648 | |
649 | // Use i2d_X509 to convert the X509 to an array. |
650 | int length = q_i2d_X509(a: x509, b: nullptr); |
651 | QByteArray array; |
652 | array.resize(size: length); |
653 | char *data = array.data(); |
654 | char **dataP = &data; |
655 | unsigned char **dataPu = (unsigned char **)dataP; |
656 | if (q_i2d_X509(a: x509, b: dataPu) < 0) |
657 | return QByteArray(); |
658 | |
659 | if (format == QSsl::Der) |
660 | return array; |
661 | |
662 | // Convert to Base64 - wrap at 64 characters. |
663 | array = array.toBase64(); |
664 | QByteArray tmp; |
665 | for (int i = 0; i <= array.size() - 64; i += 64) { |
666 | tmp += QByteArray::fromRawData(array.data() + i, size: 64); |
667 | tmp += '\n'; |
668 | } |
669 | if (int remainder = array.size() % 64) { |
670 | tmp += QByteArray::fromRawData(array.data() + array.size() - remainder, size: remainder); |
671 | tmp += '\n'; |
672 | } |
673 | |
674 | return BEGINCERTSTRING "\n" + tmp + ENDCERTSTRING "\n" ; |
675 | } |
676 | |
677 | QString QSslCertificatePrivate::text_from_X509(X509 *x509) |
678 | { |
679 | if (!x509) { |
680 | qCWarning(lcSsl, "QSslSocketBackendPrivate::text_from_X509: null X509" ); |
681 | return QString(); |
682 | } |
683 | |
684 | QByteArray result; |
685 | BIO *bio = q_BIO_new(a: q_BIO_s_mem()); |
686 | if (!bio) |
687 | return QString(); |
688 | |
689 | q_X509_print(a: bio, b: x509); |
690 | |
691 | QVarLengthArray<char, 16384> data; |
692 | int count = q_BIO_read(a: bio, b: data.data(), c: 16384); |
693 | if ( count > 0 ) { |
694 | result = QByteArray( data.data(), count ); |
695 | } |
696 | |
697 | q_BIO_free(a: bio); |
698 | |
699 | return QString::fromLatin1(str: result); |
700 | } |
701 | |
702 | QByteArray QSslCertificatePrivate::asn1ObjectId(ASN1_OBJECT *object) |
703 | { |
704 | char buf[80]; // The openssl docs a buffer length of 80 should be more than enough |
705 | q_OBJ_obj2txt(buf, buf_len: sizeof(buf), obj: object, no_name: 1); // the 1 says always use the oid not the long name |
706 | |
707 | return QByteArray(buf); |
708 | } |
709 | |
710 | |
711 | QByteArray QSslCertificatePrivate::asn1ObjectName(ASN1_OBJECT *object) |
712 | { |
713 | int nid = q_OBJ_obj2nid(a: object); |
714 | if (nid != NID_undef) |
715 | return QByteArray(q_OBJ_nid2sn(a: nid)); |
716 | |
717 | return asn1ObjectId(object); |
718 | } |
719 | |
720 | static QMultiMap<QByteArray, QString> _q_mapFromX509Name(X509_NAME *name) |
721 | { |
722 | QMultiMap<QByteArray, QString> info; |
723 | for (int i = 0; i < q_X509_NAME_entry_count(a: name); ++i) { |
724 | X509_NAME_ENTRY *e = q_X509_NAME_get_entry(a: name, b: i); |
725 | |
726 | QByteArray name = QSslCertificatePrivate::asn1ObjectName(object: q_X509_NAME_ENTRY_get_object(a: e)); |
727 | unsigned char *data = nullptr; |
728 | int size = q_ASN1_STRING_to_UTF8(a: &data, b: q_X509_NAME_ENTRY_get_data(a: e)); |
729 | info.insert(akey: name, avalue: QString::fromUtf8(str: (char*)data, size)); |
730 | #if QT_CONFIG(opensslv11) |
731 | q_CRYPTO_free(str: data, file: nullptr, line: 0); |
732 | #else |
733 | q_CRYPTO_free(data); |
734 | #endif |
735 | } |
736 | |
737 | return info; |
738 | } |
739 | |
740 | QSslCertificate QSslCertificatePrivate::QSslCertificate_from_X509(X509 *x509) |
741 | { |
742 | QSslCertificate certificate; |
743 | if (!x509 || !QSslSocket::supportsSsl()) |
744 | return certificate; |
745 | |
746 | ASN1_TIME *nbef = q_X509_getm_notBefore(a: x509); |
747 | ASN1_TIME *naft = q_X509_getm_notAfter(a: x509); |
748 | |
749 | certificate.d->notValidBefore = q_getTimeFromASN1(aTime: nbef); |
750 | certificate.d->notValidAfter = q_getTimeFromASN1(aTime: naft); |
751 | certificate.d->null = false; |
752 | certificate.d->x509 = q_X509_dup(a: x509); |
753 | |
754 | return certificate; |
755 | } |
756 | |
757 | static bool matchLineFeed(const QByteArray &pem, int *offset) |
758 | { |
759 | char ch = 0; |
760 | |
761 | // ignore extra whitespace at the end of the line |
762 | while (*offset < pem.size() && (ch = pem.at(i: *offset)) == ' ') |
763 | ++*offset; |
764 | |
765 | if (ch == '\n') { |
766 | *offset += 1; |
767 | return true; |
768 | } |
769 | if (ch == '\r' && pem.size() > (*offset + 1) && pem.at(i: *offset + 1) == '\n') { |
770 | *offset += 2; |
771 | return true; |
772 | } |
773 | return false; |
774 | } |
775 | |
776 | QList<QSslCertificate> QSslCertificatePrivate::certificatesFromPem(const QByteArray &pem, int count) |
777 | { |
778 | QList<QSslCertificate> certificates; |
779 | QSslSocketPrivate::ensureInitialized(); |
780 | |
781 | int offset = 0; |
782 | while (count == -1 || certificates.size() < count) { |
783 | int startPos = pem.indexOf(BEGINCERTSTRING, from: offset); |
784 | if (startPos == -1) |
785 | break; |
786 | startPos += sizeof(BEGINCERTSTRING) - 1; |
787 | if (!matchLineFeed(pem, offset: &startPos)) |
788 | break; |
789 | |
790 | int endPos = pem.indexOf(ENDCERTSTRING, from: startPos); |
791 | if (endPos == -1) |
792 | break; |
793 | |
794 | offset = endPos + sizeof(ENDCERTSTRING) - 1; |
795 | if (offset < pem.size() && !matchLineFeed(pem, offset: &offset)) |
796 | break; |
797 | |
798 | QByteArray decoded = QByteArray::fromBase64( |
799 | base64: QByteArray::fromRawData(pem.data() + startPos, size: endPos - startPos)); |
800 | const unsigned char *data = (const unsigned char *)decoded.data(); |
801 | |
802 | if (X509 *x509 = q_d2i_X509(a: nullptr, b: &data, c: decoded.size())) { |
803 | certificates << QSslCertificate_from_X509(x509); |
804 | q_X509_free(a: x509); |
805 | } |
806 | } |
807 | |
808 | return certificates; |
809 | } |
810 | |
811 | QList<QSslCertificate> QSslCertificatePrivate::certificatesFromDer(const QByteArray &der, int count) |
812 | { |
813 | QList<QSslCertificate> certificates; |
814 | QSslSocketPrivate::ensureInitialized(); |
815 | |
816 | const unsigned char *data = (const unsigned char *)der.data(); |
817 | int size = der.size(); |
818 | |
819 | while (size > 0 && (count == -1 || certificates.size() < count)) { |
820 | if (X509 *x509 = q_d2i_X509(a: nullptr, b: &data, c: size)) { |
821 | certificates << QSslCertificate_from_X509(x509); |
822 | q_X509_free(a: x509); |
823 | } else { |
824 | break; |
825 | } |
826 | size -= ((const char *)data - der.data()); |
827 | } |
828 | |
829 | return certificates; |
830 | } |
831 | |
832 | QT_END_NAMESPACE |
833 | |