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");
364 QSslConfiguration sslConfig = d->socket.sslConfiguration();
366 #if QT_VERSION >= 0x040800
368 sslConfig.setSslOption(QSsl::SslOptionDisableCompression,
true);
373 KTcpSocket::SslVersions alreadyTriedSslVersions = trySslVersion;
380 d->socket.connectToHost(host, port);
381 const bool connectOk = d->socket.waitForConnected(timeout > -1 ? timeout : -1);
383 kDebug(7027) <<
"Socket: state=" << d->socket.state()
384 <<
", error=" << d->socket.error()
385 <<
", connected?" << connectOk;
389 *errorString = host + QLatin1String(
": ") + d->socket.errorString();
390 switch (d->socket.error()) {
406 d->ip = d->socket.peerAddress().toString();
407 d->port = d->socket.peerPort();
410 SslResult res = d->startTLSInternal(trySslVersion, sslConfig, timeout);
413 trySslVersion = KTcpSocket::SecureProtocols;
414 alreadyTriedSslVersions |= trySslVersion;
419 trySslVersion = KTcpSocket::TlsV1;
420 alreadyTriedSslVersions |= trySslVersion;
425 trySslVersion = KTcpSocket::SslV3;
426 alreadyTriedSslVersions |= trySslVersion;
432 if (res & ResultFailed) {
434 *errorString =
i18nc(
"%1 is a host name",
"%1: SSL negotiation failed", host);
441 setMetaData(QLatin1String(
"{internal~currenthost}LastUsedSslVersion"),
472 d->socket.disconnectFromHost();
474 d->socket.waitForDisconnected(-1);
495 return d->socket.atEnd();
505 TCPSlaveBase::SslResult TCPSlaveBase::TcpSlaveBasePrivate::startTLSInternal (
KTcpSocket::SslVersion version,
506 const QSslConfiguration& sslConfig,
507 int waitForEncryptedTimeout)
509 q->selectClientCertificate();
514 #if QT_VERSION >= 0x040800
515 kDebug(7027) <<
"Trying SSL handshake with protocol:" << version
516 <<
", SSL compression ON:" << sslConfig.testSslOption(QSsl::SslOptionDisableCompression);
519 socket.setAdvertisedSslVersion(version);
522 if (!sslConfig.isNull())
523 socket.setSslConfiguration(sslConfig);
529 socket.ignoreSslErrors();
530 socket.startClientEncryption();
531 const bool encryptionStarted = socket.waitForEncrypted(waitForEncryptedTimeout);
537 || cipher.
isNull() || cipher.
usedBits() == 0 || socket.peerCertificateChain().isEmpty()) {
540 kDebug(7029) <<
"Initial SSL handshake failed. encryptionStarted is"
541 << encryptionStarted <<
", cipher.isNull() is" << cipher.
isNull()
542 <<
", cipher.usedBits() is" << cipher.
usedBits()
543 <<
", length of certificate chain is" << socket.peerCertificateChain().count()
544 <<
", the socket says:" << socket.errorString()
545 <<
"and the list of SSL errors contains"
546 << socket.sslErrors().count() <<
"items.";
547 Q_FOREACH(
const KSslError& sslError, socket.sslErrors()) {
550 return ResultFailed | ResultFailedEarly;
553 kDebug(7029) <<
"Cipher info - "
554 <<
" advertised SSL protocol version" << socket.advertisedSslVersion()
555 <<
" negotiated SSL protocol version" << socket.negotiatedSslVersion()
559 <<
" name:" << cipher.
name()
561 <<
" usedBits:" << cipher.
usedBits();
563 sslErrors = socket.sslErrors();
575 q->sendAndKeepMetaData();
577 SslResult rc = q->verifyServerCertificate();
578 if (rc & ResultFailed) {
581 kDebug(7029) <<
"server certificate verification failed.";
582 socket.disconnectFromHost();
584 }
else if (rc & ResultOverridden) {
585 kDebug(7029) <<
"server certificate verification failed but continuing at user's request.";
589 if (q->metaData(
"ssl_activate_warnings") ==
"TRUE"
590 && q->metaData(
"ssl_was_in_use") ==
"FALSE"
591 && sslSettings.warnOnEnter()) {
593 int msgResult = q->messageBox(
i18n(
"You are about to enter secure mode. "
594 "All transmissions will be encrypted "
595 "unless otherwise noted.\nThis means "
596 "that no third party will be able to "
597 "easily observe your data in transit."),
599 i18n(
"Security Information"),
600 i18n(
"Display SSL &Information"),
602 "WarnOnEnterSSLMode");
604 q->messageBox(SSLMessageBox , host);
611 void TCPSlaveBase::selectClientCertificate()
615 bool send =
false, prompt =
false,
save =
false, forcePrompt =
false;
620 if (
metaData(
"ssl_no_client_cert") ==
"TRUE")
return;
621 forcePrompt = (
metaData(
"ssl_force_cert_prompt") ==
"TRUE");
629 if (!d->kssl)
return;
636 send =
true; prompt =
false;
639 send =
false; prompt =
false;
643 send =
false; prompt =
true;
676 certname =
metaData(
"ssl_demand_certificate");
677 if (!certname.isEmpty()) {
684 if (certname.isEmpty() && !prompt && !forcePrompt)
return;
687 if (prompt || forcePrompt) {
690 QStringList::const_iterator it = certs.begin();
691 while (it != certs.end()) {
695 it = certs.erase(it);
702 if (certs.isEmpty())
return;
704 if (!QDBusConnection::sessionBus().interface()->isServiceRegistered(
"org.kde.kio.uiserver")) {
709 QDBusInterface uis(
"org.kde.kio.uiserver",
"/UIServer",
"org.kde.KIO.UIServer");
711 QDBusMessage retVal = uis.call(
"showSSLCertDialog", d->host, certs,
metaData(
"window-id").toLongLong());
712 if (retVal.type() == QDBusMessage::ReplyMessage) {
713 if (retVal.arguments().at(0).toBool()) {
714 send = retVal.arguments().at(1).toBool();
715 save = retVal.arguments().at(2).toBool();
716 certname = retVal.arguments().at(3).toString();
737 ai.
prompt =
i18n(
"Enter the certificate password:");
740 ai.
url.setHost(certname);
751 i18n(
"Unable to open the certificate. Try a new password?")))
763 if (!d->kssl->setClientCertificate(pkcs)) {
765 "client certificate for the session "
766 "failed."),
i18n(
"SSL"));
770 kDebug(7029) <<
"Client SSL certificate is being used.";
782 TCPSlaveBase::SslResult TCPSlaveBase::verifyServerCertificate()
786 if (d->sslErrors.isEmpty()) {
788 }
else if (d->sslNoUi) {
793 if (!fatalErrors.isEmpty()) {
802 QList<KSslError> remainingErrors = rule.
filterErrors(d->sslErrors);
803 if (remainingErrors.isEmpty()) {
804 kDebug(7029) <<
"Error list empty after removing errors to be ignored. Continuing.";
810 QString message =
i18n(
"The server failed the authenticity check (%1).\n\n", d->host);
811 Q_FOREACH (
const KSslError &err, d->sslErrors) {
813 message.append(
'\n');
815 message = message.trimmed();
820 i18n(
"Server Authentication"),
821 i18n(
"&Details"),
i18n(
"Co&ntinue"));
834 i18n(
"Would you like to accept this "
835 "certificate forever without "
837 i18n(
"Server Authentication"),
839 i18n(
"&Current Session only"));
840 QDateTime ruleExpiry = QDateTime::currentDateTime();
843 ruleExpiry = ruleExpiry.addYears(1000);
846 ruleExpiry = ruleExpiry.addSecs(30*60);
857 #if 0 //### need to to do something like the old code about the main and subframe stuff
858 kDebug(7029) <<
"SSL HTTP frame the parent? " <<
metaData(
"main_frame_request");
864 KSSLCertificateCache::KSSLCertificatePolicy cp =
865 d->certCache->getPolicyByCertificate(pc);
873 if (cp == KSSLCertificateCache::Unknown ||
874 cp == KSSLCertificateCache::Ambiguous) {
875 cp = KSSLCertificateCache::Prompt;
878 permacache = d->certCache->isPermanent(pc);
881 if (!_IPmatchesCN && cp == KSSLCertificateCache::Accept) {
882 cp = KSSLCertificateCache::Prompt;
889 d->certCache->addCertificate(pc, cp, permacache);
890 if (doAddHost) d->certCache->addHost(pc, d->host);
893 KSSLCertificateCache::KSSLCertificatePolicy cp =
894 d->certCache->getPolicyByCertificate(pc);
899 bool certAndIPTheSame = (d->ip ==
metaData(
"ssl_parent_ip") &&
900 pc.toString() ==
metaData(
"ssl_parent_cert"));
903 if (certAndIPTheSame) {
932 if (cp == KSSLCertificateCache::Accept) {
933 if (certAndIPTheSame) {
938 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?"),
939 i18n(
"Server Authentication"));
943 d->certCache->addHost(pc, d->host);
949 }
else if (cp == KSSLCertificateCache::Reject) {
950 messageBox(
Information,
i18n(
"SSL certificate is being rejected as requested. You can disable this in the KDE System Settings."),
951 i18n(
"Server Authentication"));
973 if (d->socket.bytesAvailable()) {
976 return d->socket.waitForReadyRead(t * 1000);
982 kWarning(7029) <<
"Caller requested non-blocking mode, but that doesn't work";
991 d->sendSslMetaData();