kio Library API Documentation

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   certList.prepend(n); 
00210   if (!permanent) {
00211     n->expires = QDateTime::currentDateTime();
00212     n->expires = n->expires.addSecs(3600);
00213   }
00214   cacheSaveToDisk();
00215 }
00216 
00217 
00218 KSSLCertificateCache::KSSLCertificatePolicy KSSLD::cacheGetPolicyByCN(QString cn) {
00219   KSSLCNode *node;
00220 
00221   for (node = certList.first(); node; node = certList.next()) {
00222     if (KSSLX509Map(node->cert->getSubject()).getValue("CN") == cn) {
00223       if (!node->permanent && node->expires < QDateTime::currentDateTime()) {
00224         certList.remove(node);
00225         cfg->deleteGroup(node->cert->getSubject());
00226         delete node;
00227         continue;
00228       }
00229       certList.remove(node);
00230       certList.prepend(node);
00231       cacheSaveToDisk();
00232       return node->policy;
00233     }
00234   }
00235   cacheSaveToDisk();
00236   return KSSLCertificateCache::Unknown;
00237 }
00238 
00239 
00240 KSSLCertificateCache::KSSLCertificatePolicy KSSLD::cacheGetPolicyByCertificate(KSSLCertificate cert) {
00241   KSSLCNode *node;
00242 
00243   for (node = certList.first(); node; node = certList.next()) {
00244     if (cert == *(node->cert)) {  
00245       if (!node->permanent && node->expires < QDateTime::currentDateTime()) {
00246         certList.remove(node);
00247         cfg->deleteGroup(node->cert->getSubject());
00248         delete node;
00249         cacheSaveToDisk();
00250         return KSSLCertificateCache::Unknown;
00251       }
00252       certList.remove(node);
00253       certList.prepend(node);
00254       return node->policy;
00255     }
00256   }
00257   return KSSLCertificateCache::Unknown;
00258 }
00259 
00260 
00261 bool KSSLD::cacheSeenCN(QString cn) {
00262   KSSLCNode *node;
00263 
00264   for (node = certList.first(); node; node = certList.next()) {
00265     if (KSSLX509Map(node->cert->getSubject()).getValue("CN") == cn) {
00266       if (!node->permanent && node->expires < QDateTime::currentDateTime()) {
00267         certList.remove(node);
00268         cfg->deleteGroup(node->cert->getSubject());
00269         delete node;
00270         cacheSaveToDisk();
00271         continue;
00272       }
00273       certList.remove(node);
00274       certList.prepend(node);
00275       return true;
00276     }
00277   }
00278   return false;
00279 }
00280 
00281 
00282 bool KSSLD::cacheSeenCertificate(KSSLCertificate cert) {
00283   KSSLCNode *node;
00284 
00285   for (node = certList.first(); node; node = certList.next()) {
00286     if (cert == *(node->cert)) {
00287       if (!node->permanent && node->expires < QDateTime::currentDateTime()) {
00288         certList.remove(node);
00289         cfg->deleteGroup(node->cert->getSubject());
00290         delete node;
00291         cacheSaveToDisk();
00292         return false;
00293       }
00294       certList.remove(node);
00295       certList.prepend(node);
00296       return true;
00297     }
00298   }
00299   return false;
00300 }
00301 
00302 
00303 bool KSSLD::cacheIsPermanent(KSSLCertificate cert) {
00304   KSSLCNode *node;
00305 
00306   for (node = certList.first(); node; node = certList.next()) {
00307     if (cert == *(node->cert)) {
00308       if (!node->permanent && node->expires < QDateTime::currentDateTime()) {
00309         certList.remove(node);
00310         cfg->deleteGroup(node->cert->getSubject());
00311         delete node;
00312         cacheSaveToDisk();
00313         return false;
00314       }
00315       certList.remove(node);
00316       certList.prepend(node);
00317       return node->permanent;
00318     }
00319   }
00320   return false;
00321 }
00322 
00323 
00324 bool KSSLD::cacheRemoveByCN(QString cn) {
00325   KSSLCNode *node;
00326   bool gotOne = false;
00327 
00328   for (node = certList.first(); node; node = certList.next()) {
00329     if (KSSLX509Map(node->cert->getSubject()).getValue("CN") == cn) {
00330       certList.remove(node);
00331       cfg->deleteGroup(node->cert->getSubject());
00332       delete node;
00333       gotOne = true;
00334     }
00335   }
00336   cacheSaveToDisk();
00337   return gotOne;
00338 }
00339 
00340 
00341 bool KSSLD::cacheRemoveByCertificate(KSSLCertificate cert) {
00342   KSSLCNode *node;
00343 
00344   for (node = certList.first(); node; node = certList.next()) {
00345     if (cert == *(node->cert)) {
00346       certList.remove(node);
00347       cfg->deleteGroup(node->cert->getSubject());
00348       delete node;
00349       cacheSaveToDisk();
00350       return true;
00351     }
00352   }
00353   return false;
00354 }
00355 
00356 
00357 bool KSSLD::cacheModifyByCN(QString cn,
00358                             KSSLCertificateCache::KSSLCertificatePolicy policy,                             bool permanent,
00359                             QDateTime expires) {
00360   KSSLCNode *node;
00361 
00362   for (node = certList.first(); node; node = certList.next()) {
00363     if (KSSLX509Map(node->cert->getSubject()).getValue("CN") == cn) {
00364       node->permanent = permanent;
00365       node->expires = expires;
00366       node->policy = policy;
00367       certList.remove(node);
00368       certList.prepend(node);
00369       cacheSaveToDisk();
00370       return true;
00371     }
00372   }
00373   return false;
00374 }
00375 
00376 
00377 bool KSSLD::cacheModifyByCertificate(KSSLCertificate cert,
00378                              KSSLCertificateCache::KSSLCertificatePolicy policy,                                     bool permanent,
00379                                      QDateTime expires) {
00380   KSSLCNode *node;
00381 
00382   for (node = certList.first(); node; node = certList.next()) {
00383     if (cert == *(node->cert)) {
00384       node->permanent = permanent;
00385       node->expires = expires;
00386       node->policy = policy;
00387       certList.remove(node);
00388       certList.prepend(node);
00389       cacheSaveToDisk();
00390       return true;
00391     }
00392   }
00393   return false;
00394 }
00395 
00396 
00397 QStringList KSSLD::cacheGetHostList(KSSLCertificate cert) {
00398   KSSLCNode *node;
00399 
00400   for (node = certList.first(); node; node = certList.next()) {
00401     if (cert == *(node->cert)) {
00402       if (!node->permanent && node->expires < QDateTime::currentDateTime()) {
00403         certList.remove(node);
00404         cfg->deleteGroup(node->cert->getSubject());
00405         delete node;
00406         cacheSaveToDisk();
00407         return QStringList();
00408       }
00409       certList.remove(node);
00410       certList.prepend(node);
00411       return node->hosts;
00412     }
00413   }
00414   return QStringList();
00415 }
00416 
00417 
00418 bool KSSLD::cacheAddHost(KSSLCertificate cert, QString host) {
00419   KSSLCNode *node;
00420 
00421   if (host.isEmpty())
00422     return true;
00423 
00424   for (node = certList.first(); node; node = certList.next()) {
00425     if (cert == *(node->cert)) {
00426       if (!node->permanent && node->expires < QDateTime::currentDateTime()) {
00427         certList.remove(node);
00428         cfg->deleteGroup(node->cert->getSubject());
00429         delete node;
00430         cacheSaveToDisk();
00431         return false;
00432       }
00433       if (!node->hosts.contains(host))
00434          node->hosts << host;
00435       certList.remove(node);
00436       certList.prepend(node);
00437       cacheSaveToDisk();
00438       return true;
00439     }
00440   }
00441   return false;
00442 }
00443 
00444 
00445 bool KSSLD::cacheRemoveHost(KSSLCertificate cert, QString host) {
00446   KSSLCNode *node;
00447 
00448   for (node = certList.first(); node; node = certList.next()) {
00449     if (cert == *(node->cert)) {
00450       if (!node->permanent && node->expires < QDateTime::currentDateTime()) {
00451         certList.remove(node);
00452         cfg->deleteGroup(node->cert->getSubject());
00453         delete node;
00454         cacheSaveToDisk();
00455         return false;
00456       }
00457       node->hosts.remove(host);
00458       certList.remove(node);
00459       certList.prepend(node);
00460       cacheSaveToDisk();
00461       return true;
00462     }
00463   }
00464   return false;
00465 }
00466 
00467 
00468 
00469 
00471 
00472 
00473 bool KSSLD::caRegenerate() {
00474 QString path = KGlobal::dirs()->saveLocation("kssl") + "/ca-bundle.crt";
00475 
00476 QFile out(path);
00477 
00478     if (!out.open(IO_WriteOnly))
00479         return false;
00480 
00481 KConfig cfg("ksslcalist", true, false);
00482 
00483 QStringList x = cfg.groupList();
00484 
00485     for (QStringList::Iterator i = x.begin();
00486                    i != x.end();
00487                    ++i) {
00488         if ((*i).isEmpty() || *i == "<default>") continue;
00489 
00490         cfg.setGroup(*i);
00491 
00492         if (!cfg.readBoolEntry("site", false)) continue;
00493 
00494         QString cert = cfg.readEntry("x509", "");
00495         if (cert.length() <= 0) continue;
00496 
00497         unsigned int xx = cert.length() - 1;
00498         for (unsigned int j = 0; j < xx/64; j++) {
00499             cert.insert(64*(j+1)+j, '\n');
00500         }
00501         out.writeBlock("-----BEGIN CERTIFICATE-----\n", 28);
00502         out.writeBlock(cert.latin1(), cert.length());
00503         out.writeBlock("\n-----END CERTIFICATE-----\n\n", 28);
00504         out.flush();
00505     }
00506 
00507 return true;
00508 }
00509 
00510 
00511 
00512 bool KSSLD::caAdd(QString certificate, bool ssl, bool email, bool code) {
00513 KSSLCertificate *x = KSSLCertificate::fromString(certificate.local8Bit());
00514 
00515     if (!x) return false;
00516 
00517 KConfig cfg("ksslcalist", false, false);
00518 
00519     cfg.setGroup(x->getSubject());
00520     cfg.writeEntry("x509", certificate);
00521     cfg.writeEntry("site", ssl);
00522     cfg.writeEntry("email", email);
00523     cfg.writeEntry("code", code);
00524 
00525     cfg.sync();
00526     delete x;
00527 
00528 return true;
00529 }
00530 
00531 
00532 QStringList KSSLD::caList() {
00533 QStringList x;
00534 KConfig cfg("ksslcalist", true, false);
00535 
00536     x = cfg.groupList();
00537     x.remove("<default>");
00538 
00539 return x;
00540 }
00541 
00542 
00543 bool KSSLD::caUseForSSL(QString subject) {
00544 KConfig cfg("ksslcalist", true, false);
00545 
00546     if (!cfg.hasGroup(subject))
00547         return false;
00548 
00549     cfg.setGroup(subject);
00550 return cfg.readBoolEntry("site", false);
00551 }
00552 
00553 
00554 
00555 bool KSSLD::caUseForEmail(QString subject) {
00556 KConfig cfg("ksslcalist", true, false);
00557 
00558     if (!cfg.hasGroup(subject))
00559         return false;
00560 
00561     cfg.setGroup(subject);
00562 return cfg.readBoolEntry("email", false);
00563 }
00564 
00565 
00566 
00567 bool KSSLD::caUseForCode(QString subject) {
00568 KConfig cfg("ksslcalist", true, false);
00569 
00570     if (!cfg.hasGroup(subject))
00571         return false;
00572 
00573     cfg.setGroup(subject);
00574 return cfg.readBoolEntry("code", false);
00575 }
00576 
00577 
00578 bool KSSLD::caRemove(QString subject) {
00579 KConfig cfg("ksslcalist", false, false);
00580     if (!cfg.hasGroup(subject))
00581         return false;
00582 
00583     cfg.deleteGroup(subject);
00584     cfg.sync();
00585 
00586 return true;
00587 }
00588 
00589 
00590 QString KSSLD::caGetCert(QString subject) {
00591 KConfig cfg("ksslcalist", true, false);
00592     if (!cfg.hasGroup(subject))
00593         return QString::null;
00594 
00595     cfg.setGroup(subject);
00596 
00597 return cfg.readEntry("x509", QString::null);
00598 }
00599 
00600 
00601 bool KSSLD::caSetUse(QString subject, bool ssl, bool email, bool code) {
00602 KConfig cfg("ksslcalist", false, false);
00603     if (!cfg.hasGroup(subject))
00604         return false;
00605 
00606     cfg.setGroup(subject);
00607 
00608     cfg.writeEntry("site", ssl);
00609     cfg.writeEntry("email", email);
00610     cfg.writeEntry("code", code);
00611     cfg.sync();
00612 
00613 return true;
00614 }
00615 
00616 
00617 
00619 
00620 
00621 
00622 
00623 
00625 
00626 #include "kssld.moc"
00627 
00628 
00629 /*
00630 
00631   DESIGN     - KSSLCertificateCache
00632   ------
00633 
00634   This is the first implementation and I think this cache actually needs
00635   experimentation to determine which implementation works best.  My current
00636   options are:
00637 
00638    (1) Store copies of the X509 certificates in a QPtrList using a self
00639        organizing heuristic as described by Munro and Suwanda.
00640    (2) Store copies of the X509 certificates in a tree structure, perhaps
00641        a redblack tree, avl tree, or even just a simple binary tree.
00642    (3) Store the CN's in a tree or list and use them as a hash to retrieve
00643        the X509 certificates.
00644    (4) Create "nodes" containing the X509 certificate and place them in
00645        two structures concurrently, one organized by CN, the other by
00646        X509 serial number.
00647 
00648   This implementation uses (1).  (4) is definitely attractive, but I don't
00649   think it will be necessary to go so crazy with performance, and perhaps
00650   end up performing poorly in situations where there are very few entries in
00651   the cache (which is most likely the case most of the time).  The style of
00652   heuristic is move-to-front, not swap-forward.  This seems to make more
00653   sense because the typical user will hit a site at least a few times in a
00654   row before moving to a new one.
00655 
00656   What I worry about most with respect to performance is that cryptographic
00657   routines are expensive and if we have to perform them on each X509
00658   certificate until the right one is found, we will perform poorly.
00659 
00660   All in all, this code is actually quite crucial for performance on SSL
00661   website, especially those with many image files loaded via SSL.  If a
00662   site loads 15 images, we will have to run through this code 15 times.
00663   A heuristic for self organization will make each successive lookup faster.
00664   Sounds good, doesn't it?
00665 
00666   DO NOT ATTEMPT TO GUESS WHICH CERTIFICATES ARE ACCEPTIBLE IN YOUR CODE!!
00667   ALWAYS USE THE CACHE.  IT MAY CHECK THINGS THAT YOU DON'T THINK OF, AND
00668   ALSO IF THERE IS A BUG IN THE CHECKING CODE, IF IT IS ALL CONTAINED IN
00669   THIS LIBRARY, A MINOR FIX WILL FIX ALL APPLICATIONS.
00670  */
00671 
KDE Logo
This file is part of the documentation for kdelibs Version 3.1.0.
Documentation copyright © 1996-2002 the KDE developers.
Generated on Wed Oct 8 12:21:32 2003 by doxygen 1.2.18 written by Dimitri van Heesch, © 1997-2001