~ubuntu-branches/ubuntu/precise/kde4libs/precise-security

« back to all changes in this revision

Viewing changes to kio/kio/tcpslavebase.cpp

  • Committer: Package Import Robot
  • Author(s): Marc Deslauriers
  • Date: 2012-07-13 09:32:36 UTC
  • mfrom: (264.1.2 precise-proposed)
  • Revision ID: package-import@ubuntu.com-20120713093236-1h9o46oxo19pgy2w
Tags: 4:4.8.4a-0ubuntu0.2
No change rebuild in the security pocket

Show diffs side-by-side

added added

removed removed

Lines of Context:
29
29
#include <config.h>
30
30
 
31
31
#include <kdebug.h>
 
32
#include <kconfiggroup.h>
32
33
#include <ksslcertificatemanager.h>
33
34
#include <ksslsettings.h>
34
35
#include <kmessagebox.h>
40
41
#include <QtCore/QTime>
41
42
#include <QtNetwork/QTcpSocket>
42
43
#include <QtNetwork/QHostInfo>
 
44
#include <QtNetwork/QSslConfiguration>
43
45
#include <QtDBus/QtDBus>
44
46
 
45
47
 
140
142
        sslMetaData.insert("ssl_peer_chain", peerCertChain);
141
143
        sendSslMetaData();
142
144
    }
143
 
    
 
145
 
144
146
    void clearSslMetaData()
145
147
    {
146
148
        sslMetaData.clear();
147
149
        sslMetaData.insert("ssl_in_use", "FALSE");
148
150
        sendSslMetaData();
149
151
    }
150
 
    
 
152
 
151
153
    void sendSslMetaData()
152
154
    {
153
155
        MetaData::ConstIterator it = sslMetaData.constBegin();
156
158
        }
157
159
    }
158
160
 
 
161
    SslResult startTLSInternal(KTcpSocket::SslVersion sslVersion,
 
162
                               const QSslConfiguration& configuration = QSslConfiguration(),
 
163
                               int waitForEncryptedTimeout = -1);
 
164
 
159
165
    TCPSlaveBase* q;
160
166
 
161
167
    bool isBlocking;
173
179
    bool sslNoUi; // If true, we just drop the connection silently
174
180
                  // if SSL certificate check fails in some way.
175
181
    QList<KSslError> sslErrors;
176
 
    
 
182
 
177
183
    MetaData sslMetaData;
178
184
};
179
185
 
343
349
        }
344
350
    }
345
351
 
346
 
    KTcpSocket::SslVersion trySslVersion = KTcpSocket::TlsV1;
347
 
    const int timeout = readTimeout() * 1000;
 
352
    /*
 
353
      By default the SSL handshake attempt uses these settings in the order shown:
 
354
 
 
355
      1.) Protocol: KTcpSocket::SecureProtocols   SSL compression: ON  (DEFAULT)
 
356
      2.) Protocol: KTcpSocket::SecureProtocols   SSL compression: OFF
 
357
      3.) Protocol: KTcpSocket::TlsV1             SSL compression: ON
 
358
      4.) Protocol: KTcpSocket::TlsV1             SSL compression: OFF
 
359
      5.) Protocol: KTcpSocket::SslV3             SSL compression: ON
 
360
      6.) Protocol: KTcpSocket::SslV3             SSL compression: OFF
 
361
 
 
362
      If any combination other than the one marked DEFAULT is used to complete
 
363
      the SSL handshake, then that combination will be cached using KIO's internal
 
364
      meta-data mechanism in order to speed up future connections to the same host.
 
365
    */
 
366
 
 
367
    QSslConfiguration sslConfig = d->socket.sslConfiguration();
 
368
#if QT_VERSION >= 0x040800
 
369
    const bool isSslCompressionDisabled = sslConfig.testSslOption(QSsl::SslOptionDisableCompression);
 
370
    const bool shouldSslCompressBeDisabled = config()->readEntry("LastUsedSslDisableCompressionFlag", isSslCompressionDisabled);
 
371
    sslConfig.setSslOption(QSsl::SslOptionDisableCompression, shouldSslCompressBeDisabled);
 
372
#endif
 
373
    
 
374
    const int lastSslVerson = config()->readEntry("LastUsedSslVersion", static_cast<int>(KTcpSocket::SecureProtocols));
 
375
    KTcpSocket::SslVersion trySslVersion = static_cast<KTcpSocket::SslVersion>(lastSslVerson);
 
376
    KTcpSocket::SslVersions alreadyTriedSslVersions = trySslVersion;
 
377
 
 
378
    const int timeout = (connectTimeout() * 1000);
348
379
    while (true) {
349
380
        disconnectFromHost();  //Reset some state, even if we are already disconnected
350
381
        d->host = host;
352
383
        d->socket.connectToHost(host, port);
353
384
        const bool connectOk = d->socket.waitForConnected(timeout > -1 ? timeout : -1);
354
385
 
355
 
        kDebug(7029) << ", Socket state:" << d->socket.state()
356
 
                     << "Socket error:" << d->socket.error()
357
 
                     << ", Connection succeeded:" << connectOk;
 
386
        kDebug(7027) << "Socket: state=" << d->socket.state()
 
387
                     << ", error=" << d->socket.error()
 
388
                     << ", connected?" << connectOk;
358
389
 
359
390
        if (d->socket.state() != KTcpSocket::ConnectedState) {
360
391
            if (errorString)
379
410
        d->port = d->socket.peerPort();
380
411
 
381
412
        if (d->autoSSL) {
382
 
            SslResult res = startTLSInternal(trySslVersion);
383
 
            if ((res & ResultFailed) && (res & ResultFailedEarly)
384
 
                && (trySslVersion == KTcpSocket::TlsV1)) {
385
 
                trySslVersion = KTcpSocket::SslV3;
386
 
                continue;
387
 
                //### SSL 2.0 is (close to) dead and it's a good thing, too.
 
413
            SslResult res = d->startTLSInternal(trySslVersion, sslConfig, 30000 /*30 secs timeout*/);
 
414
            if ((res & ResultFailed) && (res & ResultFailedEarly)) {
 
415
#if QT_VERSION >= 0x040800
 
416
                if (!sslConfig.testSslOption(QSsl::SslOptionDisableCompression)) {
 
417
                    sslConfig.setSslOption(QSsl::SslOptionDisableCompression, true);
 
418
                    continue;
 
419
                }
 
420
#endif
 
421
 
 
422
                if (!(alreadyTriedSslVersions & KTcpSocket::SecureProtocols)) {
 
423
                    trySslVersion = KTcpSocket::SecureProtocols;
 
424
                    alreadyTriedSslVersions |= trySslVersion;
 
425
#if QT_VERSION >= 0x040800
 
426
                    sslConfig.setSslOption(QSsl::SslOptionDisableCompression, false);
 
427
#endif
 
428
                    continue;
 
429
                }
 
430
 
 
431
                if (!(alreadyTriedSslVersions & KTcpSocket::TlsV1)) {
 
432
                    trySslVersion = KTcpSocket::TlsV1;
 
433
                    alreadyTriedSslVersions |= trySslVersion;
 
434
#if QT_VERSION >= 0x040800
 
435
                    sslConfig.setSslOption(QSsl::SslOptionDisableCompression, false);
 
436
#endif
 
437
                    continue;
 
438
                }
 
439
 
 
440
                if (!(alreadyTriedSslVersions & KTcpSocket::SslV3)) {
 
441
                    trySslVersion = KTcpSocket::SslV3;
 
442
                    alreadyTriedSslVersions |= trySslVersion;
 
443
#if QT_VERSION >= 0x040800
 
444
                    sslConfig.setSslOption(QSsl::SslOptionDisableCompression, false);
 
445
#endif
 
446
                    continue;
 
447
                }
388
448
            }
 
449
 
 
450
            //### SSL 2.0 is (close to) dead and it's a good thing, too.
389
451
            if (res & ResultFailed) {
390
452
                if (errorString)
391
453
                    *errorString = i18nc("%1 is a host name", "%1: SSL negotiation failed", host);
392
454
                return ERR_COULD_NOT_CONNECT;
393
455
            }
394
456
        }
 
457
        // If the SSL handshake was done with anything protocol other than the default,
 
458
        // save that information so that any subsequent requests do not have to do thesame thing.
 
459
        if (trySslVersion != KTcpSocket::SecureProtocols && lastSslVerson == KTcpSocket::SecureProtocols) {
 
460
            setMetaData(QLatin1String("{internal~currenthost}LastUsedSslVersion"),
 
461
                        QString::number(trySslVersion));
 
462
        }
 
463
#if QT_VERSION >= 0x040800
 
464
        if (sslConfig.testSslOption(QSsl::SslOptionDisableCompression) && !shouldSslCompressBeDisabled) {
 
465
            setMetaData(QLatin1String("{internal~currenthost}LastUsedSslDisableCompressionFlag"),
 
466
                        QString::number(true));
 
467
        }
 
468
#endif
395
469
        return 0;
396
470
    }
397
471
    Q_ASSERT(false);
 
472
    // Code flow never gets here but let's make the compiler happy.
 
473
    // More: the stack allocation of QSslSettings seems to be confusing the compiler;
 
474
    //       in fact, any non-POD allocation does. 
 
475
    //       even a 'return 0;' directly after the allocation (so before the while(true))
 
476
    //       is ignored. definitely seems to be a compiler bug? - aseigo
 
477
    return 0;
398
478
}
399
479
 
400
480
void TCPSlaveBase::disconnectFromHost()
444
524
{
445
525
    if (d->usingSSL)
446
526
        return false;
447
 
    return startTLSInternal(KTcpSocket::TlsV1) & ResultOk;
 
527
    return d->startTLSInternal(KTcpSocket::TlsV1) & ResultOk;
448
528
}
449
529
 
450
530
// Find out if a hostname matches an SSL certificate's Common Name (including wildcards)
491
571
    return true;
492
572
}
493
573
 
494
 
TCPSlaveBase::SslResult TCPSlaveBase::startTLSInternal(uint v_)
 
574
TCPSlaveBase::SslResult TCPSlaveBase::TcpSlaveBasePrivate::startTLSInternal (KTcpSocket::SslVersion version,
 
575
                                                                             const QSslConfiguration& sslConfig,
 
576
                                                                             int waitForEncryptedTimeout)
495
577
{
496
 
    KTcpSocket::SslVersion sslVersion = static_cast<KTcpSocket::SslVersion>(v_);
497
 
    selectClientCertificate();
 
578
    q->selectClientCertificate();
498
579
 
499
580
    //setMetaData("ssl_session_id", d->kssl->session()->toString());
500
581
    //### we don't support session reuse for now...
501
 
 
502
 
    d->usingSSL = true;
503
 
 
504
 
    d->socket.setAdvertisedSslVersion(sslVersion);
 
582
    usingSSL = true;
 
583
#if QT_VERSION >= 0x040800
 
584
    kDebug(7027) << "Trying SSL handshake with protocol:" << version
 
585
                 << ", SSL compression ON:" << sslConfig.testSslOption(QSsl::SslOptionDisableCompression);
 
586
#endif
 
587
    // Set the SSL version to use...
 
588
    socket.setAdvertisedSslVersion(version);
 
589
 
 
590
    // Set SSL configuration information
 
591
    if (!sslConfig.isNull())
 
592
        socket.setSslConfiguration(sslConfig);
505
593
 
506
594
    /* Usually ignoreSslErrors() would be called in the slot invoked by the sslErrors()
507
595
       signal but that would mess up the flow of control. We will check for errors
508
596
       anyway to decide if we want to continue connecting. Otherwise ignoreSslErrors()
509
597
       before connecting would be very insecure. */
510
 
    d->socket.ignoreSslErrors();
511
 
    d->socket.startClientEncryption();
512
 
    const bool encryptionStarted = d->socket.waitForEncrypted(-1);
 
598
    socket.ignoreSslErrors();
 
599
    socket.startClientEncryption();
 
600
    const bool encryptionStarted = socket.waitForEncrypted(waitForEncryptedTimeout);
513
601
 
514
602
    //Set metadata, among other things for the "SSL Details" dialog
515
 
    KSslCipher cipher = d->socket.sessionCipher();
 
603
    KSslCipher cipher = socket.sessionCipher();
516
604
 
517
 
    if (!encryptionStarted || d->socket.encryptionMode() != KTcpSocket::SslClientMode
518
 
        || cipher.isNull() || cipher.usedBits() == 0 || d->socket.peerCertificateChain().isEmpty()) {
519
 
        d->usingSSL = false;
520
 
        d->clearSslMetaData();
 
605
    if (!encryptionStarted || socket.encryptionMode() != KTcpSocket::SslClientMode
 
606
        || cipher.isNull() || cipher.usedBits() == 0 || socket.peerCertificateChain().isEmpty()) {
 
607
        usingSSL = false;
 
608
        clearSslMetaData();
521
609
        kDebug(7029) << "Initial SSL handshake failed. encryptionStarted is"
522
610
                     << encryptionStarted << ", cipher.isNull() is" << cipher.isNull()
523
611
                     << ", cipher.usedBits() is" << cipher.usedBits()
524
 
                     << ", length of certificate chain is" << d->socket.peerCertificateChain().count()
525
 
                     << ", the socket says:" << d->socket.errorString()
 
612
                     << ", length of certificate chain is" << socket.peerCertificateChain().count()
 
613
                     << ", the socket says:" << socket.errorString()
526
614
                     << "and the list of SSL errors contains"
527
 
                     << d->socket.sslErrors().count() << "items.";
 
615
                     << socket.sslErrors().count() << "items.";
 
616
        Q_FOREACH(const KSslError& sslError, socket.sslErrors()) {
 
617
            kDebug(7029) << "SSL ERROR: (" << sslError.error() << ")" << sslError.errorString();
 
618
        }
528
619
        return ResultFailed | ResultFailedEarly;
529
620
    }
530
621
 
531
622
    kDebug(7029) << "Cipher info - "
532
 
                 << " advertised SSL protocol version" << d->socket.advertisedSslVersion()
533
 
                 << " negotiated SSL protocol version" << d->socket.negotiatedSslVersion()
 
623
                 << " advertised SSL protocol version" << socket.advertisedSslVersion()
 
624
                 << " negotiated SSL protocol version" << socket.negotiatedSslVersion()
534
625
                 << " authenticationMethod:" << cipher.authenticationMethod()
535
626
                 << " encryptionMethod:" << cipher.encryptionMethod()
536
627
                 << " keyExchangeMethod:" << cipher.keyExchangeMethod()
541
632
    // Since we connect by IP (cf. KIO::HostInfo) the SSL code will not recognize
542
633
    // that the site certificate belongs to the domain. We therefore do the
543
634
    // domain<->certificate matching here.
544
 
    d->sslErrors = d->socket.sslErrors();
545
 
    QSslCertificate peerCert = d->socket.peerCertificateChain().first();
546
 
    QMutableListIterator<KSslError> it(d->sslErrors);
 
635
    sslErrors = socket.sslErrors();
 
636
    QSslCertificate peerCert = socket.peerCertificateChain().first();
 
637
    QMutableListIterator<KSslError> it(sslErrors);
547
638
    while (it.hasNext()) {
548
639
        // As of 4.4.0 Qt does not assign a certificate to the QSslError it emits
549
640
        // *in the case of HostNameMismatch*. A HostNameMismatch, however, will always
563
654
    domainPatterns += peerCert.alternateSubjectNames().values(QSsl::DnsEntry);
564
655
    bool names_match = false;
565
656
    foreach (const QString &dp, domainPatterns) {
566
 
        if (isMatchingHostname(dp, d->host)) {
 
657
        if (isMatchingHostname(dp, host)) {
567
658
            names_match = true;
568
659
            break;
569
660
        }
570
661
    }
571
662
    if (!names_match) {
572
 
        d->sslErrors.insert(0, KSslError(KSslError::HostNameMismatch, peerCert));
 
663
        sslErrors.insert(0, KSslError(KSslError::HostNameMismatch, peerCert));
573
664
    }
574
665
 
575
666
    // TODO: review / rewrite / remove the comment
581
672
    // from here, for example. And Konqi will be the second application to connect
582
673
    // to the slave.
583
674
    // Therefore we choose to have our metadata and send it, too :)
584
 
    d->setSslMetaData();
585
 
    sendAndKeepMetaData();
 
675
    setSslMetaData();
 
676
    q->sendAndKeepMetaData();
586
677
 
587
 
    SslResult rc = verifyServerCertificate();
 
678
    SslResult rc = q->verifyServerCertificate();
588
679
    if (rc & ResultFailed) {
589
 
        d->usingSSL = false;
590
 
        d->clearSslMetaData();
 
680
        usingSSL = false;
 
681
        clearSslMetaData();
591
682
        kDebug(7029) << "server certificate verification failed.";
592
 
        d->socket.disconnectFromHost();     //Make the connection fail (cf. ignoreSslErrors())
 
683
        socket.disconnectFromHost();     //Make the connection fail (cf. ignoreSslErrors())
593
684
        return ResultFailed;
594
685
    } else if (rc & ResultOverridden) {
595
686
        kDebug(7029) << "server certificate verification failed but continuing at user's request.";
596
687
    }
597
688
 
598
689
    //"warn" when starting SSL/TLS
599
 
    if (metaData("ssl_activate_warnings") == "TRUE"
600
 
        && metaData("ssl_was_in_use") == "FALSE"
601
 
        && d->sslSettings.warnOnEnter()) {
 
690
    if (q->metaData("ssl_activate_warnings") == "TRUE"
 
691
        && q->metaData("ssl_was_in_use") == "FALSE"
 
692
        && sslSettings.warnOnEnter()) {
602
693
 
603
 
        int msgResult = messageBox(i18n("You are about to enter secure mode. "
 
694
        int msgResult = q->messageBox(i18n("You are about to enter secure mode. "
604
695
                                        "All transmissions will be encrypted "
605
696
                                        "unless otherwise noted.\nThis means "
606
697
                                        "that no third party will be able to "
611
702
                                   i18n("C&onnect"),
612
703
                                   "WarnOnEnterSSLMode");
613
704
        if (msgResult == KMessageBox::Yes) {
614
 
            messageBox(SSLMessageBox /*==the SSL info dialog*/, d->host);
 
705
            q->messageBox(SSLMessageBox /*==the SSL info dialog*/, host);
615
706
        }
616
707
    }
617
708