• Skip to content
  • Skip to link menu
KDE 4.6 API Reference
  • KDE API Reference
  • kdelibs
  • KDE Home
  • Contact Us
 

KDECore

ksslcertificatemanager.cpp

Go to the documentation of this file.
00001 /* This file is part of the KDE project
00002  *
00003  * Copyright (C) 2007, 2008, 2010 Andreas Hartmetz <ahartmetz@gmail.com>
00004  *
00005  * This library is free software; you can redistribute it and/or
00006  * modify it under the terms of the GNU Library General Public
00007  * License as published by the Free Software Foundation; either
00008  * version 2 of the License, or (at your option) any later version.
00009  *
00010  * This library is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013  * Library General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU Library General Public License
00016  * along with this library; see the file COPYING.LIB.  If not, write to
00017  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00018  * Boston, MA 02110-1301, USA.
00019  */
00020 
00021 
00022 #include "ksslcertificatemanager.h"
00023 #include "ktcpsocket.h"
00024 #include "ktcpsocket_p.h"
00025 #include <kconfig.h>
00026 #include <kconfiggroup.h>
00027 #include <kdebug.h>
00028 #include <kglobal.h>
00029 #include <klocale.h>
00030 #include <kstandarddirs.h>
00031 #include <ktoolinvocation.h>
00032 
00033 #include <QtDBus/QtDBus>
00034 
00035 #include "kssld/kssld_interface.h"
00036 #include "ksslcertificatemanager_p.h"
00037 
00038 /*
00039   Config file format:
00040 [<MD5-Digest>]
00041 <Host> = <Date> <List of ignored errors>
00042 #for example
00043 #mail.kdab.net =  ExpireUTC 2008-08-20T18:22:14, SelfSigned, Expired
00044 #very.old.com =  ExpireUTC 2008-08-20T18:22:14, TooWeakEncryption <- not actually planned to implement
00045 #clueless.admin.com =  ExpireUTC 2008-08-20T18:22:14, HostNameMismatch
00046 #
00047 #Wildcard syntax
00048 #* = ExpireUTC 2008-08-20T18:22:14, SelfSigned
00049 #*.kdab.net = ExpireUTC 2008-08-20T18:22:14, SelfSigned
00050 #mail.kdab.net = ExpireUTC 2008-08-20T18:22:14, All <- not implemented
00051 #* = ExpireUTC 9999-12-31T23:59:59, Reject  #we know that something is wrong with that certificate
00052 CertificatePEM = <PEM-encoded certificate> #host entries are all lowercase, thus no clashes
00053 
00054  */
00055 
00056 // TODO GUI for managing exception rules
00057 
00058 class KSslCertificateRulePrivate
00059 {
00060 public:
00061     QSslCertificate certificate;
00062     QString hostName;
00063     bool isRejected;
00064     QDateTime expiryDateTime;
00065     QList<KSslError::Error> ignoredErrors;
00066 };
00067 
00068 
00069 KSslCertificateRule::KSslCertificateRule(const QSslCertificate &cert, const QString &hostName)
00070  : d(new KSslCertificateRulePrivate())
00071 {
00072     d->certificate = cert;
00073     d->hostName = hostName;
00074     d->isRejected = false;
00075 }
00076 
00077 
00078 KSslCertificateRule::KSslCertificateRule(const KSslCertificateRule &other)
00079  : d(new KSslCertificateRulePrivate())
00080 {
00081     *d = *other.d;
00082 }
00083 
00084 
00085 KSslCertificateRule::~KSslCertificateRule()
00086 {
00087     delete d;
00088 }
00089 
00090 
00091 KSslCertificateRule &KSslCertificateRule::operator=(const KSslCertificateRule &other)
00092 {
00093     *d = *other.d;
00094     return *this;
00095 }
00096 
00097 
00098 QSslCertificate KSslCertificateRule::certificate() const
00099 {
00100     return d->certificate;
00101 }
00102 
00103 
00104 QString KSslCertificateRule::hostName() const
00105 {
00106     return d->hostName;
00107 }
00108 
00109 
00110 void KSslCertificateRule::setExpiryDateTime(const QDateTime &dateTime)
00111 {
00112     d->expiryDateTime = dateTime;
00113 }
00114 
00115 
00116 QDateTime KSslCertificateRule::expiryDateTime() const
00117 {
00118     return d->expiryDateTime;
00119 }
00120 
00121 
00122 void KSslCertificateRule::setRejected(bool rejected)
00123 {
00124     d->isRejected = rejected;
00125 }
00126 
00127 
00128 bool KSslCertificateRule::isRejected() const
00129 {
00130     return d->isRejected;
00131 }
00132 
00133 
00134 bool KSslCertificateRule::isErrorIgnored(KSslError::Error error) const
00135 {
00136     foreach (KSslError::Error ignoredError, d->ignoredErrors)
00137         if (error == ignoredError)
00138             return true;
00139 
00140     return false;
00141 }
00142 
00143 
00144 void KSslCertificateRule::setIgnoredErrors(const QList<KSslError::Error> &errors)
00145 {
00146     d->ignoredErrors.clear();
00147     //### Quadratic runtime, woohoo! Use a QSet if that should ever be an issue.
00148     foreach(KSslError::Error e, errors)
00149         if (!isErrorIgnored(e))
00150             d->ignoredErrors.append(e);
00151 }
00152 
00153 
00154 void KSslCertificateRule::setIgnoredErrors(const QList<KSslError> &errors)
00155 {
00156     QList<KSslError::Error> el;
00157     foreach(const KSslError &e, errors)
00158         el.append(e.error());
00159     setIgnoredErrors(el);
00160 }
00161 
00162 
00163 QList<KSslError::Error> KSslCertificateRule::ignoredErrors() const
00164 {
00165     return d->ignoredErrors;
00166 }
00167 
00168 
00169 QList<KSslError::Error> KSslCertificateRule::filterErrors(const QList<KSslError::Error> &errors) const
00170 {
00171     QList<KSslError::Error> ret;
00172     foreach (KSslError::Error error, errors) {
00173         if (!isErrorIgnored(error))
00174             ret.append(error);
00175     }
00176     return ret;
00177 }
00178 
00179 
00180 QList<KSslError> KSslCertificateRule::filterErrors(const QList<KSslError> &errors) const
00181 {
00182     QList<KSslError> ret;
00183     foreach (const KSslError &error, errors) {
00184         if (!isErrorIgnored(error.error()))
00185             ret.append(error);
00186     }
00187     return ret;
00188 }
00189 
00190 
00192 
00193 KSslCertificateManagerPrivate::KSslCertificateManagerPrivate()
00194  : config(QString::fromLatin1("ksslcertificatemanager"), KConfig::SimpleConfig),
00195    iface(new org::kde::KSSLDInterface(QString::fromLatin1("org.kde.kded"),
00196                                       QString::fromLatin1("/modules/kssld"),
00197                                       QDBusConnection::sessionBus())),
00198    isCertListLoaded(false),
00199    userCertDir(KGlobal::dirs()->saveLocation("data", QString::fromLatin1("kssl/userCaCertificates/")))
00200 {
00201     // set Qt's set to empty; this is protected by the lock in K_GLOBAL_STATIC.
00202     QSslSocket::setDefaultCaCertificates(QList<QSslCertificate>());
00203 }
00204 
00205 KSslCertificateManagerPrivate::~KSslCertificateManagerPrivate()
00206 {
00207     delete iface;
00208     iface = 0;
00209 }
00210 
00211 void KSslCertificateManagerPrivate::loadDefaultCaCertificates()
00212 {
00213     defaultCaCertificates.clear();
00214 
00215     if (!KGlobal::hasMainComponent()) {
00216         Q_ASSERT(false);
00217         return;                 // we need KGlobal::dirs() available
00218     }
00219 
00220     QList<QSslCertificate> certs = QSslSocket::systemCaCertificates();
00221 
00222     KConfig config(QString::fromLatin1("ksslcablacklist"), KConfig::SimpleConfig);
00223     KConfigGroup group = config.group("Blacklist of CA Certificates");
00224 
00225     certs.append(QSslCertificate::fromPath(userCertDir + QLatin1String("*"), QSsl::Pem,
00226                                            QRegExp::Wildcard));
00227     foreach (const QSslCertificate &cert, certs) {
00228         const QByteArray digest = cert.digest().toHex();
00229         if (!group.hasKey(digest.constData())) {
00230             defaultCaCertificates += cert;
00231         }
00232     }
00233 
00234     isCertListLoaded = true;
00235 }
00236 
00237 
00238 bool KSslCertificateManagerPrivate::addCertificate(const KSslCaCertificate &in)
00239 {
00240     kDebug(7029);
00241     // cannot add a certificate to the system store
00242     if (in.store == KSslCaCertificate::SystemStore) {
00243         Q_ASSERT(false);
00244         return false;
00245     }
00246     if (knownCerts.contains(in.certHash)) {
00247         Q_ASSERT(false);
00248         return false;
00249     }
00250 
00251     QString certFilename = userCertDir + QString::fromLatin1(in.certHash);
00252     kDebug(7029) << certFilename;
00253     QFile certFile(certFilename);
00254     if (certFile.open(QIODevice::ReadOnly)) {
00255         return false;
00256     }
00257     if (!certFile.open(QIODevice::WriteOnly)) {
00258         return false;
00259     }
00260     if (certFile.write(in.cert.toPem()) < 1) {
00261         return false;
00262     }
00263     knownCerts.insert(in.certHash);
00264 
00265     updateCertificateBlacklisted(in);
00266 
00267     return true;
00268 }
00269 
00270 
00271 bool KSslCertificateManagerPrivate::removeCertificate(const KSslCaCertificate &old)
00272 {
00273     kDebug(7029);
00274     // cannot remove a certificate from the system store
00275     if (old.store == KSslCaCertificate::SystemStore) {
00276         Q_ASSERT(false);
00277         return false;
00278     }
00279 
00280     if (!QFile::remove(userCertDir + QString::fromLatin1(old.certHash))) {
00281 
00282         // suppose somebody copied a certificate file into userCertDir without changing the
00283         // filename to the digest.
00284         // the rest of the code will work fine because it loads all certificate files from
00285         // userCertDir without asking for the name, we just can't remove the certificate using
00286         // its digest as filename - so search the whole directory.
00287         // if the certificate was added with the digest as name *and* with a different name, we
00288         // still fail to remove it completely at first try - BAD USER! BAD!
00289 
00290         bool removed = false;
00291         QDir dir(userCertDir);
00292         foreach (const QString &certFilename, dir.entryList(QDir::Files)) {
00293             const QString certPath = userCertDir + certFilename;
00294             QList<QSslCertificate> certs = QSslCertificate::fromPath(certPath);
00295 
00296             if (!certs.isEmpty() && certs.at(0).digest().toHex() == old.certHash) {
00297                 if (QFile::remove(certPath)) {
00298                     removed = true;
00299                 } else {
00300                     // maybe the file is readable but not writable
00301                     return false;
00302                 }
00303             }
00304         }
00305         if (!removed) {
00306             // looks like the file is not there
00307             return false;
00308         }
00309     }
00310 
00311     // note that knownCerts *should* need no updating due to the way setAllCertificates() works -
00312     // it should never call addCertificate and removeCertificate for the same cert in one run
00313 
00314     // clean up the blacklist
00315     setCertificateBlacklisted(old.certHash, false);
00316 
00317     return true;
00318 }
00319 
00320 static bool certLessThan(const KSslCaCertificate &cacert1, const KSslCaCertificate &cacert2)
00321 {
00322     if (cacert1.store != cacert2.store) {
00323         // SystemStore is numerically smaller so the system certs come first; this is important
00324         // so that system certificates come first in case the user added an already-present
00325         // certificate as a user certificate.
00326         return cacert1.store < cacert2.store;
00327     }
00328     return cacert1.certHash < cacert2.certHash;
00329 }
00330 
00331 void KSslCertificateManagerPrivate::setAllCertificates(const QList<KSslCaCertificate> &certsIn)
00332 {
00333     Q_ASSERT(knownCerts.isEmpty());
00334     QList<KSslCaCertificate> in = certsIn;
00335     QList<KSslCaCertificate> old = allCertificates();
00336     qSort(in.begin(), in.end(), certLessThan);
00337     qSort(old.begin(), old.end(), certLessThan);
00338 
00339     for (int ii = 0, oi = 0; ii < in.size() || oi < old.size(); ii++, oi++) {
00340         // look at all elements in both lists, even if we reach the end of one early.
00341         if (ii >= in.size()) {
00342             removeCertificate(old.at(oi));
00343             continue;
00344         } else if (oi >= old.size()) {
00345             addCertificate(in.at(ii));
00346             continue;
00347         }
00348 
00349         if (certLessThan (old.at(oi), in.at(ii))) {
00350             // the certificate in "old" is not in "in". only advance the index of "old".
00351             removeCertificate(old.at(oi));
00352             ii--;
00353         } else if (certLessThan(in.at(ii), old.at(oi))) {
00354             // the certificate in "in" is not in "old". only advance the index of "in".
00355             addCertificate(in.at(ii));
00356             oi--;
00357         } else { // in.at(ii) "==" old.at(oi)
00358             if (in.at(ii).cert != old.at(oi).cert) {
00359                 // hash collision, be prudent(?) and don't do anything.
00360             } else {
00361                 knownCerts.insert(old.at(oi).certHash);
00362                 if (in.at(ii).isBlacklisted != old.at(oi).isBlacklisted) {
00363                     updateCertificateBlacklisted(in.at(ii));
00364                 }
00365             }
00366         }
00367     }
00368     knownCerts.clear();
00369     QMutexLocker certListLocker(&certListMutex);
00370     isCertListLoaded = false;
00371     loadDefaultCaCertificates();
00372 }
00373 
00374 QList<KSslCaCertificate> KSslCertificateManagerPrivate::allCertificates() const
00375 {
00376     kDebug(7029);
00377     QList<KSslCaCertificate> ret;
00378     foreach (const QSslCertificate &cert, QSslSocket::systemCaCertificates()) {
00379         ret += KSslCaCertificate(cert, KSslCaCertificate::SystemStore, false);
00380     }
00381 
00382     foreach (const QSslCertificate &cert, QSslCertificate::fromPath(userCertDir + QLatin1String("/*"),
00383                                                                     QSsl::Pem, QRegExp::Wildcard)) {
00384         ret += KSslCaCertificate(cert, KSslCaCertificate::UserStore, false);
00385     }
00386 
00387     KConfig config(QString::fromLatin1("ksslcablacklist"), KConfig::SimpleConfig);
00388     KConfigGroup group = config.group("Blacklist of CA Certificates");
00389     for (int i = 0; i < ret.size(); i++) {
00390         if (group.hasKey(ret[i].certHash.constData())) {
00391             ret[i].isBlacklisted = true;
00392             kDebug(7029) << "is blacklisted";
00393         }
00394     }
00395 
00396     return ret;
00397 }
00398 
00399 
00400 bool KSslCertificateManagerPrivate::updateCertificateBlacklisted(const KSslCaCertificate &cert)
00401 {
00402     return setCertificateBlacklisted(cert.certHash, cert.isBlacklisted);
00403 }
00404 
00405 
00406 bool KSslCertificateManagerPrivate::setCertificateBlacklisted(const QByteArray &certHash,
00407                                                               bool isBlacklisted)
00408 {
00409     kDebug(7029) << isBlacklisted;
00410     KConfig config(QString::fromLatin1("ksslcablacklist"), KConfig::SimpleConfig);
00411     KConfigGroup group = config.group("Blacklist of CA Certificates");
00412     if (isBlacklisted) {
00413         // TODO check against certificate list ?
00414         group.writeEntry(certHash.constData(), QString());
00415     } else {
00416         if (!group.hasKey(certHash.constData())) {
00417             return false;
00418         }
00419         group.deleteEntry(certHash.constData());
00420     }
00421 
00422     return true;
00423 }
00424 
00425 
00426 class KSslCertificateManagerContainer
00427 {
00428 public:
00429     KSslCertificateManager sslCertificateManager;
00430 };
00431 
00432 K_GLOBAL_STATIC(KSslCertificateManagerContainer, g_instance)
00433 
00434 
00435 KSslCertificateManager::KSslCertificateManager()
00436  : d(new KSslCertificateManagerPrivate())
00437 {
00438     // Make sure kded is running
00439     if (!QDBusConnection::sessionBus().interface()->isServiceRegistered(QString::fromLatin1("org.kde.kded"))) {
00440         KToolInvocation::klauncher(); // this calls startKdeinit
00441     }
00442 }
00443 
00444 
00445 KSslCertificateManager::~KSslCertificateManager()
00446 {
00447     delete d;
00448 }
00449 
00450 
00451 //static
00452 KSslCertificateManager *KSslCertificateManager::self()
00453 {
00454     return &g_instance->sslCertificateManager;
00455 }
00456 
00457 
00458 void KSslCertificateManager::setRule(const KSslCertificateRule &rule)
00459 {
00460     d->iface->setRule(rule);
00461 }
00462 
00463 
00464 void KSslCertificateManager::clearRule(const KSslCertificateRule &rule)
00465 {
00466     d->iface->clearRule(rule);
00467 }
00468 
00469 
00470 void KSslCertificateManager::clearRule(const QSslCertificate &cert, const QString &hostName)
00471 {
00472     d->iface->clearRule(cert, hostName);
00473 }
00474 
00475 
00476 KSslCertificateRule KSslCertificateManager::rule(const QSslCertificate &cert,
00477                                                  const QString &hostName) const
00478 {
00479     return d->iface->rule(cert, hostName);
00480 }
00481 
00482 
00483 QList<QSslCertificate> KSslCertificateManager::caCertificates() const
00484 {
00485     QMutexLocker certLocker(&d->certListMutex);
00486     if (!d->isCertListLoaded) {
00487         d->loadDefaultCaCertificates();
00488     }
00489     return d->defaultCaCertificates;
00490 }
00491 
00492 
00493 //static
00494 QList<KSslError> KSslCertificateManager::nonIgnorableErrors(const QList<KSslError> &/*e*/)
00495 {
00496     QList<KSslError> ret;
00497     // ### add filtering here...
00498     return ret;
00499 }
00500 
00501 //static
00502 QList<KSslError::Error> KSslCertificateManager::nonIgnorableErrors(const QList<KSslError::Error> &/*e*/)
00503 {
00504     QList<KSslError::Error> ret;
00505     // ### add filtering here...
00506     return ret;
00507 }
00508 
00509 QList<KSslCaCertificate> _allKsslCaCertificates(KSslCertificateManager *cm)
00510 {
00511     return KSslCertificateManagerPrivate::get(cm)->allCertificates();
00512 }
00513 
00514 void _setAllKsslCaCertificates(KSslCertificateManager *cm, const QList<KSslCaCertificate> &certsIn)
00515 {
00516     KSslCertificateManagerPrivate::get(cm)->setAllCertificates(certsIn);
00517 }
00518 
00519 #include "kssld/kssld_interface.moc"

KDECore

Skip menu "KDECore"
  • Main Page
  • Modules
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.7.3
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal