kssld.cpp Source File (kio Library)

| # kio Library API Documentation
Overview | Class Hierarchy | Classes | Classes (annotated) | Members | Source Files

# kssld.cpp

    00001 /*
    00002    This file is part of the KDE libraries
    00003
    00004    Copyright (c) 2001 George Staikos <staikos@kde.org>
    00005
    00006    This library is free software; you can redistribute it and/or
    00007    modify it under the terms of the GNU Library General Public
    00008    License as published by the Free Software Foundation; either
    00009    version 2 of the License, or (at your option) any later version.
    00010
    00011    This library is distributed in the hope that it will be useful,
    00012    but WITHOUT ANY WARRANTY; without even the implied warranty of
    00013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    00014    Library General Public License for more details.
    00015
    00016    You should have received a copy of the GNU Library General Public License
    00017    along with this library; see the file COPYING.LIB.  If not, write to
    00018    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    00019    Boston, MA 02111-1307, USA.
    00020
    00021 */
    00022
    00023 #ifdef HAVE_CONFIG_H
    00024 #include <config.h>
    00025 #endif
    00026
    00027 #include <qtimer.h>
    00028
    00029 #include "kssld.h"
    00030 #include <kconfig.h>
    00031 #include <ksimpleconfig.h>
    00032 #include <ksslcertchain.h>
    00033 #include <ksslcertificate.h>
    00034 #include <ksslx509map.h>
    00035 #include <qptrlist.h>
    00036 #include <sys/types.h>
    00037 #include <sys/stat.h>
    00038 #include <stdlib.h>
    00039 #include <pwd.h>
    00040 #include <unistd.h>
    00041 #include <qfile.h>
    00042 #include <qsortedlist.h>
    00043 #include <kglobal.h>
    00044 #include <kstandarddirs.h>
    00045 #include <kdebug.h>
    00046 #include <qdatetime.h>
    00047
    00048 #include <kmdcodec.h>
    00049 #include <kopenssl.h>
    00050
    00051 // See design notes at end
    00052
    00053 extern "C" {
    00054    KDEDModule *create_kssld(const QCString &name) {
    00055            return new KSSLD(name);
    00056    }
    00057
    00058    void *__kde_do_unload;
    00059 }
    00060
    00061
    00062 KSSLD::KSSLD(const QCString &name) : KDEDModule(name)
    00063 {
    00064 // ----------------------- FOR THE CACHE ------------------------------------
    00065   cfg = new KSimpleConfig("ksslpolicies", false);
    00066   if (!KGlobal::dirs()->addResourceType("kssl", KStandardDirs::kde_default("data") + "kssl")) {
    00067      //    kdDebug(7029) << "Error adding (kssl, share/apps/kssl)" << endl;
    00068   }
    00069   cacheLoadDefaultPolicies();
    00070   certList.setAutoDelete(false);
    00071   kossl = KOSSL::self();
    00072
    00073 // ----------------------- FOR THE HOME -------------------------------------
    00074 }
    00075
    00076
    00077 KSSLD::~KSSLD()
    00078 {
    00079 // ----------------------- FOR THE CACHE ------------------------------------
    00080   cacheClearList();
    00081   delete cfg;
    00082
    00083 // ----------------------- FOR THE HOME -------------------------------------
    00084 }
    00085
    00086
    00087
    00088
    00089 // A node in the cache
    00090 class KSSLCNode {
    00091 public:
    00092   KSSLCertificate *cert;
    00093   KSSLCertificateCache::KSSLCertificatePolicy policy;
    00094   bool permanent;
    00095   QDateTime expires;
    00096   QStringList hosts;
    00097   KSSLCNode() { cert = NULL; policy = KSSLCertificateCache::Unknown;
    00098                 permanent = true; }
    00099   ~KSSLCNode() { if (cert) delete cert; }
    00100 };
    00101
    00102
    00103
    00104 void KSSLD::cacheSaveToDisk() {
    00105   KSSLCNode *node;
    00106
    00107   for (node = certList.first(); node; node = certList.next()) {
    00108     if (node->permanent || node->expires > QDateTime::currentDateTime()) {
    00109       // First convert to a binary format and then write the kconfig entry
    00110       // write the (CN, policy, cert) to KSimpleConfig
    00111       cfg->setGroup(node->cert->getSubject());
    00112       cfg->writeEntry("Certificate", node->cert->toString());
    00113       cfg->writeEntry("Policy", node->policy);
    00114       cfg->writeEntry("Expires", node->expires);
    00115       cfg->writeEntry("Permanent", node->permanent);
    00116       cfg->writeEntry("Hosts", node->hosts);
    00117       // Also write the chain
    00118       QStringList qsl;
    00119       QPtrList<KSSLCertificate> cl = node->cert->chain().getChain();
    00120       for (KSSLCertificate *c = cl.first(); c != 0; c = cl.next()) {
    00121          //kdDebug() << "Certificate in chain: " <<  c->toString() << endl;
    00122          qsl << c->toString();
    00123       }
    00124       cl.setAutoDelete(true);
    00125       cfg->writeEntry("Chain", qsl);
    00126     }
    00127   }
    00128
    00129   cfg->sync();
    00130
    00131   // insure proper permissions -- contains sensitive data
    00132   QString cfgName(KGlobal::dirs()->findResource("config", "ksslpolicies"));
    00133   if (!cfgName.isEmpty())
    00134     ::chmod(QFile::encodeName(cfgName), 0600);
    00135 }
    00136
    00137
    00138 void KSSLD::cacheReload() {
    00139         cacheClearList();
    00140         delete cfg;
    00141         cfg = new KSimpleConfig("ksslpolicies", false);
    00142         cacheLoadDefaultPolicies();
    00143 }
    00144
    00145
    00146 void KSSLD::cacheClearList() {
    00147   KSSLCNode *node;
    00148
    00149   for (node = certList.first(); node; node = certList.next()) {
    00150     certList.remove(node);
    00151     delete node;
    00152   }
    00153 }
    00154
    00155
    00156 void KSSLD::cacheLoadDefaultPolicies() {
    00157   QStringList groups = cfg->groupList();
    00158
    00159   for (QStringList::Iterator i = groups.begin();
    00160                              i != groups.end();
    00161                              ++i) {
    00162     if ((*i).length() == 0) continue;
    00163     cfg->setGroup(*i);
    00164
    00165     // remove it if it has expired
    00166     if (!cfg->readBoolEntry("Permanent") && cfg->readDateTimeEntry("Expires") < QDateTime::currentDateTime()) {
    00167        cfg->deleteGroup(*i);
    00168        continue;
    00169     }
    00170
    00171     QCString encodedCert = cfg->readEntry("Certificate").local8Bit();
    00172     KSSLCertificate *newCert = KSSLCertificate::fromString(encodedCert);
    00173     if (!newCert) continue;
    00174     KSSLCNode *n = new KSSLCNode;
    00175     n->cert = newCert;
    00176     n->policy = (KSSLCertificateCache::KSSLCertificatePolicy)
    00177                 cfg->readNumEntry("Policy");
    00178     n->permanent = cfg->readBoolEntry("Permanent");
    00179     n->expires = cfg->readDateTimeEntry("Expires");
    00180     n->hosts = cfg->readListEntry("Hosts");
    00181     newCert->chain().setChain(cfg->readListEntry("Chain"));
    00182     certList.append(n);
    00183   }
    00184 }
    00185
    00186
    00187 void KSSLD::cacheAddCertificate(KSSLCertificate cert,
    00188          KSSLCertificateCache::KSSLCertificatePolicy policy, bool permanent) {
    00189   KSSLCNode *node;
    00190
    00191   for (node = certList.first(); node; node = certList.next()) {
    00192     if (cert == *(node->cert)) {
    00193       node->policy = policy;
    00194       node->permanent = permanent;
    00195       if (!permanent) {
    00196         node->expires = QDateTime::currentDateTime();
    00197 // FIXME: make this configurable
    00198         node->expires = node->expires.addSecs(3600);
    00199       }
    00200       cacheSaveToDisk();
    00201       return;
    00202     }
    00203   }
    00204
    00205   KSSLCNode *n = new KSSLCNode;
    00206   n->cert = cert.replicate();
    00207   n->policy = policy;
    00208   n->permanent = permanent;
    00209   cacheRemoveByCN(KSSLX509Map(n->cert->getSubject()).getValue("CN")); // remove the old one
    00210   certList.prepend(n);
    00211   if (!permanent) {
    00212     n->expires = QDateTime::currentDateTime();
    00213     n->expires = n->expires.addSecs(3600);
    00214   }
    00215   cacheSaveToDisk();
    00216 }
    00217
    00218
    00219 KSSLCertificateCache::KSSLCertificatePolicy KSSLD::cacheGetPolicyByCN(QString cn) {
    00220   KSSLCNode *node;
    00221
    00222   for (node = certList.first(); node; node = certList.next()) {
    00223     if (KSSLX509Map(node->cert->getSubject()).getValue("CN") == cn) {
    00224       if (!node->permanent && node->expires < QDateTime::currentDateTime()) {
    00225         certList.remove(node);
    00226         cfg->deleteGroup(node->cert->getSubject());
    00227         delete node;
    00228         continue;
    00229       }
    00230       certList.remove(node);
    00231       certList.prepend(node);
    00232       cacheSaveToDisk();
    00233       return node->policy;
    00234     }
    00235   }
    00236   cacheSaveToDisk();
    00237   return KSSLCertificateCache::Unknown;
    00238 }
    00239
    00240
    00241 KSSLCertificateCache::KSSLCertificatePolicy KSSLD::cacheGetPolicyByCertificate(KSSLCertificate cert) {
    00242   KSSLCNode *node;
    00243
    00244   for (node = certList.first(); node; node = certList.next()) {
    00245     if (cert == *(node->cert)) {
    00246       if (!node->permanent && node->expires < QDateTime::currentDateTime()) {
    00247         certList.remove(node);
    00248         cfg->deleteGroup(node->cert->getSubject());
    00249         delete node;
    00250         cacheSaveToDisk();
    00251         return KSSLCertificateCache::Unknown;
    00252       }
    00253       certList.remove(node);
    00254       certList.prepend(node);
    00255       return node->policy;
    00256     }
    00257   }
    00258   return KSSLCertificateCache::Unknown;
    00259 }
    00260
    00261
    00262 bool KSSLD::cacheSeenCN(QString cn) {
    00263   KSSLCNode *node;
    00264
    00265   for (node = certList.first(); node; node = certList.next()) {
    00266     if (KSSLX509Map(node->cert->getSubject()).getValue("CN") == cn) {
    00267       if (!node->permanent && node->expires < QDateTime::currentDateTime()) {
    00268         certList.remove(node);
    00269         cfg->deleteGroup(node->cert->getSubject());
    00270         delete node;
    00271         cacheSaveToDisk();
    00272         continue;
    00273       }
    00274       certList.remove(node);
    00275       certList.prepend(node);
    00276       return true;
    00277     }
    00278   }
    00279   return false;
    00280 }
    00281
    00282
    00283 bool KSSLD::cacheSeenCertificate(KSSLCertificate cert) {
    00284   KSSLCNode *node;
    00285
    00286   for (node = certList.first(); node; node = certList.next()) {
    00287     if (cert == *(node->cert)) {
    00288       if (!node->permanent && node->expires < QDateTime::currentDateTime()) {
    00289         certList.remove(node);
    00290         cfg->deleteGroup(node->cert->getSubject());
    00291         delete node;
    00292         cacheSaveToDisk();
    00293         return false;
    00294       }
    00295       certList.remove(node);
    00296       certList.prepend(node);
    00297       return true;
    00298     }
    00299   }
    00300   return false;
    00301 }
    00302
    00303
    00304 bool KSSLD::cacheIsPermanent(KSSLCertificate cert) {
    00305   KSSLCNode *node;
    00306
    00307   for (node = certList.first(); node; node = certList.next()) {
    00308     if (cert == *(node->cert)) {
    00309       if (!node->permanent && node->expires < QDateTime::currentDateTime()) {
    00310         certList.remove(node);
    00311         cfg->deleteGroup(node->cert->getSubject());
    00312         delete node;
    00313         cacheSaveToDisk();
    00314         return false;
    00315       }
    00316       certList.remove(node);
    00317       certList.prepend(node);
    00318       return node->permanent;
    00319     }
    00320   }
    00321   return false;
    00322 }
    00323
    00324
    00325 bool KSSLD::cacheRemoveByCN(QString cn) {
    00326   KSSLCNode *node;
    00327   bool gotOne = false;
    00328
    00329   for (node = certList.first(); node; node = certList.next()) {
    00330     if (KSSLX509Map(node->cert->getSubject()).getValue("CN") == cn) {
    00331       certList.remove(node);
    00332       cfg->deleteGroup(node->cert->getSubject());
    00333       delete node;
    00334       gotOne = true;
    00335     }
    00336   }
    00337   cacheSaveToDisk();
    00338   return gotOne;
    00339 }
    00340
    00341
    00342 bool KSSLD::cacheRemoveByCertificate(KSSLCertificate cert) {
    00343   KSSLCNode *node;
    00344
    00345   for (node = certList.first(); node; node = certList.next()) {
    00346     if (cert == *(node->cert)) {
    00347       certList.remove(node);
    00348       cfg->deleteGroup(node->cert->getSubject());
    00349       delete node;
    00350       cacheSaveToDisk();
    00351       return true;
    00352     }
    00353   }
    00354   return false;
    00355 }
    00356
    00357
    00358 bool KSSLD::cacheModifyByCN(QString cn,
    00359                             KSSLCertificateCache::KSSLCertificatePolicy policy,                             bool permanent,
    00360                             QDateTime expires) {
    00361   KSSLCNode *node;
    00362
    00363   for (node = certList.first(); node; node = certList.next()) {
    00364     if (KSSLX509Map(node->cert->getSubject()).getValue("CN") == cn) {
    00365       node->permanent = permanent;
    00366       node->expires = expires;
    00367       node->policy = policy;
    00368       certList.remove(node);
    00369       certList.prepend(node);
    00370       cacheSaveToDisk();
    00371       return true;
    00372     }
    00373   }
    00374   return false;
    00375 }
    00376
    00377
    00378 bool KSSLD::cacheModifyByCertificate(KSSLCertificate cert,
    00379                              KSSLCertificateCache::KSSLCertificatePolicy policy,                                     bool permanent,
    00380                                      QDateTime expires) {
    00381   KSSLCNode *node;
    00382
    00383   for (node = certList.first(); node; node = certList.next()) {
    00384     if (cert == *(node->cert)) {
    00385       node->permanent = permanent;
    00386       node->expires = expires;
    00387       node->policy = policy;
    00388       certList.remove(node);
    00389       certList.prepend(node);
    00390       cacheSaveToDisk();
    00391       return true;
    00392     }
    00393   }
    00394   return false;
    00395 }
    00396
    00397
    00398 QStringList KSSLD::cacheGetHostList(KSSLCertificate cert) {
    00399   KSSLCNode *node;
    00400
    00401   for (node = certList.first(); node; node = certList.next()) {
    00402     if (cert == *(node->cert)) {
    00403       if (!node->permanent && node->expires < QDateTime::currentDateTime()) {
    00404         certList.remove(node);
    00405         cfg->deleteGroup(node->cert->getSubject());
    00406         delete node;
    00407         cacheSaveToDisk();
    00408         return QStringList();
    00409       }
    00410       certList.remove(node);
    00411       certList.prepend(node);
    00412       return node->hosts;
    00413     }
    00414   }
    00415   return QStringList();
    00416 }
    00417
    00418
    00419 bool KSSLD::cacheAddHost(KSSLCertificate cert, QString host) {
    00420   KSSLCNode *node;
    00421
    00422   if (host.isEmpty())
    00423         return true;
    00424
    00425   for (node = certList.first(); node; node = certList.next()) {
    00426     if (cert == *(node->cert)) {
    00427       if (!node->permanent && node->expires < QDateTime::currentDateTime()) {
    00428         certList.remove(node);
    00429         cfg->deleteGroup(node->cert->getSubject());
    00430         delete node;
    00431         cacheSaveToDisk();
    00432         return false;
    00433       }
    00434       if (!node->hosts.contains(host))
    00435          node->hosts << host;
    00436       certList.remove(node);
    00437       certList.prepend(node);
    00438       cacheSaveToDisk();
    00439       return true;
    00440     }
    00441   }
    00442   return false;
    00443 }
    00444
    00445
    00446 bool KSSLD::cacheRemoveHost(KSSLCertificate cert, QString host) {
    00447   KSSLCNode *node;
    00448
    00449   for (node = certList.first(); node; node = certList.next()) {
    00450     if (cert == *(node->cert)) {
    00451       if (!node->permanent && node->expires < QDateTime::currentDateTime()) {
    00452         certList.remove(node);
    00453         cfg->deleteGroup(node->cert->getSubject());
    00454         delete node;
    00455         cacheSaveToDisk();
    00456         return false;
    00457       }
    00458       node->hosts.remove(host);
    00459       certList.remove(node);
    00460       certList.prepend(node);
    00461       cacheSaveToDisk();
    00462       return true;
    00463     }
    00464   }
    00465   return false;
    00466 }
    00467
    00468
    00469
    00470
    00472
    00473
    00474 bool KSSLD::caRegenerate() {
    00475 QString path = KGlobal::dirs()->saveLocation("kssl") + "/ca-bundle.crt";
    00476
    00477 QFile out(path);
    00478
    00479         if (!out.open(IO_WriteOnly))
    00480                 return false;
    00481
    00482 KConfig cfg("ksslcalist", true, false);
    00483
    00484 QStringList x = cfg.groupList();
    00485
    00486         for (QStringList::Iterator i = x.begin();
    00487                                    i != x.end();
    00488                                    ++i) {
    00489                 if ((*i).isEmpty() || *i == "<default>") continue;
    00490
    00491                 cfg.setGroup(*i);
    00492
    00493                 if (!cfg.readBoolEntry("site", false)) continue;
    00494
    00495                 QString cert = cfg.readEntry("x509", "");
    00496                 if (cert.length() <= 0) continue;
    00497
    00498                 unsigned int xx = cert.length() - 1;
    00499                 for (unsigned int j = 0; j < xx/64; j++) {
    00500                         cert.insert(64*(j+1)+j, '\n');
    00501                 }
    00502                 out.writeBlock("-----BEGIN CERTIFICATE-----\n", 28);
    00503                 out.writeBlock(cert.latin1(), cert.length());
    00504                 out.writeBlock("\n-----END CERTIFICATE-----\n\n", 28);
    00505                 out.flush();
    00506         }
    00507
    00508 return true;
    00509 }
    00510
    00511
    00512
    00513 bool KSSLD::caAdd(QString certificate, bool ssl, bool email, bool code) {
    00514 KSSLCertificate *x = KSSLCertificate::fromString(certificate.local8Bit());
    00515
    00516         if (!x) return false;
    00517
    00518 KConfig cfg("ksslcalist", false, false);
    00519
    00520         cfg.setGroup(x->getSubject());
    00521         cfg.writeEntry("x509", certificate);
    00522         cfg.writeEntry("site", ssl);
    00523         cfg.writeEntry("email", email);
    00524         cfg.writeEntry("code", code);
    00525
    00526         cfg.sync();
    00527         delete x;
    00528
    00529 return true;
    00530 }
    00531
    00532
    00533 QStringList KSSLD::caList() {
    00534 QStringList x;
    00535 KConfig cfg("ksslcalist", true, false);
    00536
    00537         x = cfg.groupList();
    00538         x.remove("<default>");
    00539
    00540 return x;
    00541 }
    00542
    00543
    00544 bool KSSLD::caUseForSSL(QString subject) {
    00545 KConfig cfg("ksslcalist", true, false);
    00546
    00547         if (!cfg.hasGroup(subject))
    00548                 return false;
    00549
    00550         cfg.setGroup(subject);
    00551 return cfg.readBoolEntry("site", false);
    00552 }
    00553
    00554
    00555
    00556 bool KSSLD::caUseForEmail(QString subject) {
    00557 KConfig cfg("ksslcalist", true, false);
    00558
    00559         if (!cfg.hasGroup(subject))
    00560                 return false;
    00561
    00562         cfg.setGroup(subject);
    00563 return cfg.readBoolEntry("email", false);
    00564 }
    00565
    00566
    00567
    00568 bool KSSLD::caUseForCode(QString subject) {
    00569 KConfig cfg("ksslcalist", true, false);
    00570
    00571         if (!cfg.hasGroup(subject))
    00572                 return false;
    00573
    00574         cfg.setGroup(subject);
    00575 return cfg.readBoolEntry("code", false);
    00576 }
    00577
    00578
    00579 bool KSSLD::caRemove(QString subject) {
    00580 KConfig cfg("ksslcalist", false, false);
    00581         if (!cfg.hasGroup(subject))
    00582                 return false;
    00583
    00584         cfg.deleteGroup(subject);
    00585         cfg.sync();
    00586
    00587 return true;
    00588 }
    00589
    00590
    00591 QString KSSLD::caGetCert(QString subject) {
    00592 KConfig cfg("ksslcalist", true, false);
    00593         if (!cfg.hasGroup(subject))
    00594                 return QString::null;
    00595
    00596         cfg.setGroup(subject);
    00597
    00598 return cfg.readEntry("x509", QString::null);
    00599 }
    00600
    00601
    00602 bool KSSLD::caSetUse(QString subject, bool ssl, bool email, bool code) {
    00603 KConfig cfg("ksslcalist", false, false);
    00604         if (!cfg.hasGroup(subject))
    00605                 return false;
    00606
    00607         cfg.setGroup(subject);
    00608
    00609         cfg.writeEntry("site", ssl);
    00610         cfg.writeEntry("email", email);
    00611         cfg.writeEntry("code", code);
    00612         cfg.sync();
    00613
    00614 return true;
    00615 }
    00616
    00617
    00618
    00620
    00621
    00622
    00623
    00624
    00626
    00627 #include "kssld.moc"
    00628
    00629
    00630 /*
    00631
    00632   DESIGN     - KSSLCertificateCache
    00633   ------
    00634
    00635   This is the first implementation and I think this cache actually needs
    00636   experimentation to determine which implementation works best.  My current
    00637   options are:
    00638
    00639    (1) Store copies of the X509 certificates in a QPtrList using a self
    00640        organizing heuristic as described by Munro and Suwanda.
    00641    (2) Store copies of the X509 certificates in a tree structure, perhaps
    00642        a redblack tree, avl tree, or even just a simple binary tree.
    00643    (3) Store the CN's in a tree or list and use them as a hash to retrieve
    00644        the X509 certificates.
    00645    (4) Create "nodes" containing the X509 certificate and place them in
    00646        two structures concurrently, one organized by CN, the other by
    00647        X509 serial number.
    00648
    00649   This implementation uses (1).  (4) is definitely attractive, but I don't
    00650   think it will be necessary to go so crazy with performance, and perhaps
    00651   end up performing poorly in situations where there are very few entries in
    00652   the cache (which is most likely the case most of the time).  The style of
    00653   heuristic is move-to-front, not swap-forward.  This seems to make more
    00654   sense because the typical user will hit a site at least a few times in a
    00655   row before moving to a new one.
    00656
    00657   What I worry about most with respect to performance is that cryptographic
    00658   routines are expensive and if we have to perform them on each X509
    00659   certificate until the right one is found, we will perform poorly.
    00660
    00661   All in all, this code is actually quite crucial for performance on SSL
    00662   website, especially those with many image files loaded via SSL.  If a
    00663   site loads 15 images, we will have to run through this code 15 times.
    00664   A heuristic for self organization will make each successive lookup faster.
    00665   Sounds good, doesn't it?
    00666
    00667   DO NOT ATTEMPT TO GUESS WHICH CERTIFICATES ARE ACCEPTIBLE IN YOUR CODE!!
    00668   ALWAYS USE THE CACHE.  IT MAY CHECK THINGS THAT YOU DON'T THINK OF, AND
    00669   ALSO IF THERE IS A BUG IN THE CHECKING CODE, IF IT IS ALL CONTAINED IN
    00670   THIS LIBRARY, A MINOR FIX WILL FIX ALL APPLICATIONS.
    00671  */
    00672

This file is part of the documentation for kdelibs Version 3.1.5.
Documentation copyright © 1996-2002 the KDE developers.
Generated on Wed Jan 28 13:14:25 2004 by doxygen 1.3.4 written by Dimitri van Heesch, © 1997-2001