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   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 
KDE Logo
This file is part of the documentation for kdelibs Version 3.1.4.
Documentation copyright © 1996-2002 the KDE developers.
Generated on Sun Feb 27 22:15:32 2005 by doxygen 1.3.4 written by Dimitri van Heesch, © 1997-2001