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()) {
124 if (
error.certificate() == cert) {
128 if (errorStr.endsWith(
'\t')) {
134 sslMetaData.insert(
"ssl_cert_errors", errorStr);
136 QString peerCertChain;
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;
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);
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"),
341 i18n(
"C&ontinue Loading"), QString(),
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);
414 alreadyTriedSslVersions |= trySslVersion;
420 alreadyTriedSslVersions |= trySslVersion;
426 alreadyTriedSslVersions |= trySslVersion;
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()) {
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) {
818 QDateTime ruleExpiry = QDateTime::currentDateTime();
821 i18n(
"Server Authentication"),
822 i18n(
"&Details"),
i18n(
"Co&ntinue"));
831 i18n(
"Would you like to accept this " 832 "certificate forever without " 834 i18n(
"Server Authentication"),
836 i18n(
"&Current Session only"));
839 ruleExpiry = ruleExpiry.addYears(1000);
842 ruleExpiry = ruleExpiry.addSecs(30*60);
851 kWarning() <<
"Unexpected MessageBox response received:" << msgResult;
866 #if 0 //### need to to do something like the old code about the main and subframe stuff 867 kDebug(7029) <<
"SSL HTTP frame the parent? " <<
metaData(
"main_frame_request");
873 KSSLCertificateCache::KSSLCertificatePolicy cp =
874 d->certCache->getPolicyByCertificate(pc);
882 if (cp == KSSLCertificateCache::Unknown ||
883 cp == KSSLCertificateCache::Ambiguous) {
884 cp = KSSLCertificateCache::Prompt;
887 permacache = d->certCache->isPermanent(pc);
890 if (!_IPmatchesCN && cp == KSSLCertificateCache::Accept) {
891 cp = KSSLCertificateCache::Prompt;
898 d->certCache->addCertificate(pc, cp, permacache);
899 if (doAddHost) d->certCache->addHost(pc, d->host);
902 KSSLCertificateCache::KSSLCertificatePolicy cp =
903 d->certCache->getPolicyByCertificate(pc);
908 bool certAndIPTheSame = (d->ip ==
metaData(
"ssl_parent_ip") &&
909 pc.toString() ==
metaData(
"ssl_parent_cert"));
912 if (certAndIPTheSame) {
941 if (cp == KSSLCertificateCache::Accept) {
942 if (certAndIPTheSame) {
947 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?"),
948 i18n(
"Server Authentication"));
952 d->certCache->addHost(pc, d->host);
958 }
else if (cp == KSSLCertificateCache::Reject) {
959 messageBox(
Information,
i18n(
"SSL certificate is being rejected as requested. You can disable this in the KDE System Settings."),
960 i18n(
"Server Authentication"));
982 if (d->socket.bytesAvailable()) {
985 return d->socket.waitForReadyRead(t * 1000);
991 kWarning(7029) <<
"Caller requested non-blocking mode, but that doesn't work";
1000 d->sendSslMetaData();
TCPSlaveBase(const QByteArray &protocol, const QByteArray &poolSocket, const QByteArray &appSocket, bool autoSsl=false)
Constructor.
QString authenticationMethod() const
void message(KMessage::MessageType messageType, const QString &text, const QString &caption=QString())
QString i18n(const char *text)
KUrl url
The URL for which authentication is to be stored.
virtual void virtual_hook(int id, void *data)
KSSLX509V3 & x509V3Extensions()
Access the X.509v3 parameters.
bool keepPassword
Flag to indicate the persistence of the given password.
A namespace for KIO globals.
static KSslCertificateManager * self()
bool openPasswordDialog(KIO::AuthInfo &info, const QString &errorMsg=QString())
Prompt the user for Authorization info (login & password).
ssize_t readLine(char *data, ssize_t len)
Same as read() except it reads data one line at a time.
This class is intended to make it easier to prompt for, cache and retrieve authorization information.
UnsupportedSocketOperationError
bool connectToHost(const QString &protocol, const QString &host, quint16 port)
Performs the initial TCP connection stuff and/or SSL handshaking as necessary.
void setIgnoredErrors(const QList< KSslError::Error > &errors)
bool checkCachedAuthentication(AuthInfo &info)
Checks for cached authentication based on parameters given by info.
QIODevice * socket() const
Return the socket object, if the class ever needs to do anything to it.
static QDebug kDebug(bool cond, int area=KDE_DEFAULT_DEBUG_AREA)
static QString getDefaultCertificateName(const QString &host, KSSLAuthAction *aa=NULL)
QString keyExchangeMethod() const
void setMetaData(const QString &key, const QString &value)
Sets meta-data to be send to the application before the first data() or finished() signal.
bool isUsingSsl() const
Is the current connection using SSL?
QString i18nc(const char *ctxt, const char *text)
bool hasMetaData(const QString &key) const
Queries for the existence of a certain config/meta-data entry send by the application to the slave.
void setBlocking(bool b)
Sets the mode of the connection to blocking or non-blocking.
int messageBox(MessageBoxType type, const QString &text, const QString &caption=QString(), const QString &buttonYes=i18n("&Yes"), const QString &buttonNo=i18n("&No"))
Call this to show a message box from the slave.
bool startSsl()
Start using SSL on the connection.
KSslCertificateRule rule(const QSslCertificate &cert, const QString &hostName) const
quint16 port() const
the current port for this service
void setProtocol(const QString &proto)
KConfigGroup * config()
Returns a configuration object to query config/meta-data information from.
There are two classes that specifies the protocol between application (job) and kioslave.
bool isAutoSsl() const
Will start SSL after connecting?
virtual void virtual_hook(int id, void *data)
void setExpiryDateTime(const QDateTime &dateTime)
ssize_t read(char *data, ssize_t len)
bool atEnd() const
Returns true when end of data is reached.
static KSSLPKCS12 * getCertificateByName(const QString &name, const QString &password)
bool warnOnLeave() const
Does the user want to be warned on leaving SSL mode.
QString errorString() const
bool isConnected() const
Determines whether or not we are still connected to the remote machine.
void data(const QByteArray &data)
Sends data in the slave to the job (i.e.
void disconnectFromHost()
Close the connection and forget non-permanent data like the peer host.
void written(KIO::filesize_t _bytes)
QString digestMethod() const
QList< KSslError::Error > filterErrors(const QList< KSslError::Error > &errors) const
void error(int _errid, const QString &_text)
Call to signal an error.
There are two classes that specifies the protocol between application (job) and kioslave.
static bool hasCertificateByName(const QString &name)
QString password
This is required for caching.
T readEntry(const QString &key, const T &aDefault) const
QMap< QString, QString > StringStringMap
void setRule(const KSslCertificateRule &rule)
bool certTypeSSLClient() const
Determine if this certificate can be used by an SSL client.
int supportedBits() const
QString username
This is required for caching.
static QDebug kWarning(bool cond, int area=KDE_DEFAULT_DEBUG_AREA)
static void setDefaultCertificate(const QString &name, bool send=true, bool prompt=false)
ssize_t write(const char *data, ssize_t len)
static QStringList getCertificateList()
KAction * save(const QObject *recvr, const char *slot, QObject *parent)
QString encryptionMethod() const
KSSLCertificate * getCertificate()
Get the X.509 certificate.
QString caption
The text to displayed in the title bar of the password prompting dialog.
QString metaData(const QString &key) const
Queries for config/meta-data send by the application to the slave.
static QList< KSslError > nonIgnorableErrors(const QList< KSslError > &)
QString prompt
Information to be displayed when prompting the user for authentication information.
bool waitForResponse(int t)
Wait for incoming data on the socket for the period specified by t.
QString number(KIO::filesize_t size)
Converts a size to a string representation Not unlike QString::number(...)