40 #include <QtCore/QDataStream>
41 #include <QtCore/QTime>
42 #include <QtNetwork/QTcpSocket>
43 #include <QtNetwork/QHostInfo>
44 #include <QtNetwork/QSslConfiguration>
45 #include <QtDBus/QtDBus>
55 Q_DECLARE_OPERATORS_FOR_FLAGS(TCPSlaveBase::SslResult)
91 class TCPSlaveBase::TcpSlaveBasePrivate
98 sslMetaData.insert(
"ssl_in_use",
"TRUE");
100 sslMetaData.insert(
"ssl_protocol_version",
socket.negotiatedSslVersionName());
105 sslMetaData.insert(
"ssl_cipher", sslCipher);
106 sslMetaData.insert(
"ssl_cipher_name", cipher.
name());
109 sslMetaData.insert(
"ssl_peer_ip",
ip);
113 for (
int i = 0; i < sslErrors.count(); i++) {
114 if (sslErrors[i].certificate().isNull()) {
116 socket.peerCertificateChain()[0]);
122 Q_FOREACH (
const QSslCertificate &cert,
socket.peerCertificateChain()) {
128 if (errorStr.endsWith(
'\t')) {
134 sslMetaData.insert(
"ssl_cert_errors", errorStr);
137 Q_FOREACH (
const QSslCertificate &cert,
socket.peerCertificateChain()) {
138 peerCertChain.append(cert.toPem());
139 peerCertChain.append(
'\x01');
141 peerCertChain.chop(1);
142 sslMetaData.insert(
"ssl_peer_chain", peerCertChain);
146 void clearSslMetaData()
149 sslMetaData.insert(
"ssl_in_use",
"FALSE");
153 void sendSslMetaData()
155 MetaData::ConstIterator it = sslMetaData.constBegin();
156 for (; it != sslMetaData.constEnd(); ++it) {
157 q->setMetaData(it.key(), it.value());
162 const QSslConfiguration& configuration = QSslConfiguration(),
163 int waitForEncryptedTimeout = -1);
174 QByteArray serviceName;
181 QList<KSslError> sslErrors;
195 const QByteArray &poolSocket,
196 const QByteArray &appSocket,
198 :
SlaveBase(protocol, poolSocket, appSocket),
199 d(new TcpSlaveBasePrivate(this))
201 d->isBlocking =
true;
203 d->serviceName = protocol;
205 d->autoSSL = autoSSL;
210 d->socket.setReadBufferSize(14680064);
222 ssize_t
written = d->socket.write(data, len);
224 kDebug(7027) <<
"d->socket.write() returned -1! Socket error is"
225 << d->socket.error() <<
", Socket state is" << d->socket.state();
228 bool success =
false;
231 success = d->socket.waitForBytesWritten(-1);
236 success = d->socket.waitForBytesWritten(0);
242 kDebug(7027) <<
"Write failed, will return -1! Socket error is"
243 << d->socket.error() <<
", Socket state is" << d->socket.state()
244 <<
"Return value of waitForBytesWritten() is" << success;
255 d->clearSslMetaData();
256 kDebug(7029) <<
"lost SSL connection.";
260 if (!d->socket.bytesAvailable()) {
262 d->socket.waitForReadyRead(timeout);
268 QNetworkProxy::applicationProxy().type() == QNetworkProxy::NoProxy) {
271 d->socket.waitForReadyRead(0);
274 return d->socket.read(data, len);
281 d->clearSslMetaData();
282 kDebug(7029) <<
"lost SSL connection.";
287 ssize_t readTotal = 0;
289 if (!d->socket.bytesAvailable())
290 d->socket.waitForReadyRead(timeout);
291 ssize_t readStep = d->socket.readLine(&data[readTotal], len-readTotal);
295 readTotal += readStep;
296 }
while (readTotal == 0 || data[readTotal-1] !=
'\n');
311 error(errCode, errorString);
317 d->clearSslMetaData();
320 errorString->clear();
323 d->socket.setVerificationPeerName(host);
328 if (
metaData(
"main_frame_request") ==
"TRUE"
329 &&
metaData(
"ssl_activate_warnings") ==
"TRUE"
330 &&
metaData(
"ssl_was_in_use") ==
"TRUE"
335 "mode. Transmissions will no "
336 "longer be encrypted.\nThis "
337 "means that a third party could "
338 "observe your data in transit."),
340 i18n(
"Security Information"),
342 "WarnOnLeaveSSLMode");
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);
376 KTcpSocket::SslVersions alreadyTriedSslVersions = trySslVersion;
383 d->socket.connectToHost(host, port);
384 const bool connectOk = d->socket.waitForConnected(timeout > -1 ? timeout : -1);
386 kDebug(7027) <<
"Socket: state=" << d->socket.state()
387 <<
", error=" << d->socket.error()
388 <<
", connected?" << connectOk;
392 *errorString = host + QLatin1String(
": ") + d->socket.errorString();
393 switch (d->socket.error()) {
409 d->ip = d->socket.peerAddress().toString();
410 d->port = d->socket.peerPort();
413 SslResult res = d->startTLSInternal(trySslVersion, sslConfig, 30000 );
415 #if QT_VERSION >= 0x040800
416 if (!sslConfig.testSslOption(QSsl::SslOptionDisableCompression)) {
417 sslConfig.setSslOption(QSsl::SslOptionDisableCompression,
true);
423 trySslVersion = KTcpSocket::SecureProtocols;
424 alreadyTriedSslVersions |= trySslVersion;
425 #if QT_VERSION >= 0x040800
426 sslConfig.setSslOption(QSsl::SslOptionDisableCompression,
false);
432 trySslVersion = KTcpSocket::TlsV1;
433 alreadyTriedSslVersions |= trySslVersion;
434 #if QT_VERSION >= 0x040800
435 sslConfig.setSslOption(QSsl::SslOptionDisableCompression,
false);
441 trySslVersion = KTcpSocket::SslV3;
442 alreadyTriedSslVersions |= trySslVersion;
443 #if QT_VERSION >= 0x040800
444 sslConfig.setSslOption(QSsl::SslOptionDisableCompression,
false);
451 if (res & ResultFailed) {
453 *errorString =
i18nc(
"%1 is a host name",
"%1: SSL negotiation failed", host);
460 setMetaData(QLatin1String(
"{internal~currenthost}LastUsedSslVersion"),
463 #if QT_VERSION >= 0x040800
464 if (sslConfig.testSslOption(QSsl::SslOptionDisableCompression) && !shouldSslCompressBeDisabled) {
465 setMetaData(QLatin1String(
"{internal~currenthost}LastUsedSslDisableCompressionFlag"),
497 d->socket.disconnectFromHost();
499 d->socket.waitForDisconnected(-1);
520 return d->socket.atEnd();
533 const QString cn = cnIn.toLower();
534 const QString hostname = hostnameIn.toLower();
536 const int wildcard = cn.indexOf(QLatin1Char(
'*'));
540 return cn == hostname;
542 const int firstCnDot = cn.indexOf(QLatin1Char(
'.'));
543 const int secondCnDot = cn.indexOf(QLatin1Char(
'.'), firstCnDot+1);
546 if ((-1 == secondCnDot) || (secondCnDot+1 >= cn.length()))
550 if (wildcard+1 != firstCnDot)
554 if (cn.lastIndexOf(QLatin1Char(
'*')) != wildcard)
558 if (wildcard && (hostname.leftRef(wildcard) != cn.leftRef(wildcard)))
562 if (hostname.midRef(hostname.indexOf(QLatin1Char(
'.'))) != cn.midRef(firstCnDot))
566 QHostAddress addr(hostname);
574 TCPSlaveBase::SslResult TCPSlaveBase::TcpSlaveBasePrivate::startTLSInternal (
KTcpSocket::SslVersion version,
575 const QSslConfiguration& sslConfig,
576 int waitForEncryptedTimeout)
578 q->selectClientCertificate();
583 #if QT_VERSION >= 0x040800
584 kDebug(7027) <<
"Trying SSL handshake with protocol:" << version
585 <<
", SSL compression ON:" << sslConfig.testSslOption(QSsl::SslOptionDisableCompression);
588 socket.setAdvertisedSslVersion(version);
591 if (!sslConfig.isNull())
592 socket.setSslConfiguration(sslConfig);
598 socket.ignoreSslErrors();
599 socket.startClientEncryption();
600 const bool encryptionStarted = socket.waitForEncrypted(waitForEncryptedTimeout);
606 || cipher.
isNull() || cipher.
usedBits() == 0 || socket.peerCertificateChain().isEmpty()) {
609 kDebug(7029) <<
"Initial SSL handshake failed. encryptionStarted is"
610 << encryptionStarted <<
", cipher.isNull() is" << cipher.
isNull()
611 <<
", cipher.usedBits() is" << cipher.
usedBits()
612 <<
", length of certificate chain is" << socket.peerCertificateChain().count()
613 <<
", the socket says:" << socket.errorString()
614 <<
"and the list of SSL errors contains"
615 << socket.sslErrors().count() <<
"items.";
616 Q_FOREACH(
const KSslError& sslError, socket.sslErrors()) {
619 return ResultFailed | ResultFailedEarly;
622 kDebug(7029) <<
"Cipher info - "
623 <<
" advertised SSL protocol version" << socket.advertisedSslVersion()
624 <<
" negotiated SSL protocol version" << socket.negotiatedSslVersion()
628 <<
" name:" << cipher.
name()
630 <<
" usedBits:" << cipher.
usedBits();
635 sslErrors = socket.sslErrors();
636 QSslCertificate peerCert = socket.peerCertificateChain().first();
637 QMutableListIterator<KSslError> it(sslErrors);
638 while (it.hasNext()) {
653 QStringList domainPatterns(peerCert.subjectInfo(QSslCertificate::CommonName));
654 domainPatterns += peerCert.alternateSubjectNames().values(QSsl::DnsEntry);
655 bool names_match =
false;
656 foreach (
const QString &dp, domainPatterns) {
676 q->sendAndKeepMetaData();
678 SslResult rc = q->verifyServerCertificate();
679 if (rc & ResultFailed) {
682 kDebug(7029) <<
"server certificate verification failed.";
683 socket.disconnectFromHost();
685 }
else if (rc & ResultOverridden) {
686 kDebug(7029) <<
"server certificate verification failed but continuing at user's request.";
690 if (q->metaData(
"ssl_activate_warnings") ==
"TRUE"
691 && q->metaData(
"ssl_was_in_use") ==
"FALSE"
692 && sslSettings.warnOnEnter()) {
694 int msgResult = q->messageBox(
i18n(
"You are about to enter secure mode. "
695 "All transmissions will be encrypted "
696 "unless otherwise noted.\nThis means "
697 "that no third party will be able to "
698 "easily observe your data in transit."),
700 i18n(
"Security Information"),
701 i18n(
"Display SSL &Information"),
703 "WarnOnEnterSSLMode");
705 q->messageBox(SSLMessageBox , host);
712 void TCPSlaveBase::selectClientCertificate()
716 bool send =
false, prompt =
false,
save =
false, forcePrompt =
false;
721 if (
metaData(
"ssl_no_client_cert") ==
"TRUE")
return;
722 forcePrompt = (
metaData(
"ssl_force_cert_prompt") ==
"TRUE");
730 if (!d->kssl)
return;
737 send =
true; prompt =
false;
740 send =
false; prompt =
false;
744 send =
false; prompt =
true;
777 certname =
metaData(
"ssl_demand_certificate");
778 if (!certname.isEmpty()) {
785 if (certname.isEmpty() && !prompt && !forcePrompt)
return;
788 if (prompt || forcePrompt) {
791 QStringList::const_iterator it = certs.begin();
792 while (it != certs.end()) {
796 it = certs.erase(it);
803 if (certs.isEmpty())
return;
805 if (!QDBusConnection::sessionBus().interface()->isServiceRegistered(
"org.kde.kio.uiserver")) {
810 QDBusInterface uis(
"org.kde.kio.uiserver",
"/UIServer",
"org.kde.KIO.UIServer");
812 QDBusMessage retVal = uis.call(
"showSSLCertDialog", d->host, certs,
metaData(
"window-id").toLongLong());
813 if (retVal.type() == QDBusMessage::ReplyMessage) {
814 if (retVal.arguments().at(0).toBool()) {
815 send = retVal.arguments().at(1).toBool();
816 save = retVal.arguments().at(2).toBool();
817 certname = retVal.arguments().at(3).toString();
838 ai.
prompt =
i18n(
"Enter the certificate password:");
841 ai.
url.setHost(certname);
852 i18n(
"Unable to open the certificate. Try a new password?")))
864 if (!d->kssl->setClientCertificate(pkcs)) {
866 "client certificate for the session "
867 "failed."),
i18n(
"SSL"));
871 kDebug(7029) <<
"Client SSL certificate is being used.";
883 TCPSlaveBase::SslResult TCPSlaveBase::verifyServerCertificate()
887 if (d->sslErrors.isEmpty()) {
889 }
else if (d->sslNoUi) {
894 if (!fatalErrors.isEmpty()) {
903 QList<KSslError> remainingErrors = rule.
filterErrors(d->sslErrors);
904 if (remainingErrors.isEmpty()) {
905 kDebug(7029) <<
"Error list empty after removing errors to be ignored. Continuing.";
911 QString message =
i18n(
"The server failed the authenticity check (%1).\n\n", d->host);
912 Q_FOREACH (
const KSslError &err, d->sslErrors) {
914 message.append(
'\n');
916 message = message.trimmed();
921 i18n(
"Server Authentication"),
922 i18n(
"&Details"),
i18n(
"Co&ntinue"));
935 i18n(
"Would you like to accept this "
936 "certificate forever without "
938 i18n(
"Server Authentication"),
940 i18n(
"&Current Session only"));
941 QDateTime ruleExpiry = QDateTime::currentDateTime();
944 ruleExpiry = ruleExpiry.addYears(1000);
947 ruleExpiry = ruleExpiry.addSecs(30*60);
958 #if 0 //### need to to do something like the old code about the main and subframe stuff
959 kDebug(7029) <<
"SSL HTTP frame the parent? " <<
metaData(
"main_frame_request");
965 KSSLCertificateCache::KSSLCertificatePolicy cp =
966 d->certCache->getPolicyByCertificate(pc);
974 if (cp == KSSLCertificateCache::Unknown ||
975 cp == KSSLCertificateCache::Ambiguous) {
976 cp = KSSLCertificateCache::Prompt;
979 permacache = d->certCache->isPermanent(pc);
982 if (!_IPmatchesCN && cp == KSSLCertificateCache::Accept) {
983 cp = KSSLCertificateCache::Prompt;
990 d->certCache->addCertificate(pc, cp, permacache);
991 if (doAddHost) d->certCache->addHost(pc, d->host);
994 KSSLCertificateCache::KSSLCertificatePolicy cp =
995 d->certCache->getPolicyByCertificate(pc);
1000 bool certAndIPTheSame = (d->ip ==
metaData(
"ssl_parent_ip") &&
1001 pc.toString() ==
metaData(
"ssl_parent_cert"));
1004 if (certAndIPTheSame) {
1033 if (cp == KSSLCertificateCache::Accept) {
1034 if (certAndIPTheSame) {
1039 i18n(
"You have indicated that you wish to accept this certificate, but it is not issued to the server who is presenting it. Do you wish to continue loading?"),
1040 i18n(
"Server Authentication"));
1044 d->certCache->addHost(pc, d->host);
1050 }
else if (cp == KSSLCertificateCache::Reject) {
1051 messageBox(
Information,
i18n(
"SSL certificate is being rejected as requested. You can disable this in the KDE System Settings."),
1052 i18n(
"Server Authentication"));
1074 if (d->socket.bytesAvailable()) {
1077 return d->socket.waitForReadyRead(t * 1000);
1083 kWarning(7029) <<
"Caller requested non-blocking mode, but that doesn't work";
1092 d->sendSslMetaData();