KDECore
kssld.cpp
Go to the documentation of this file.
00001 /* 00002 This file is part of the KDE libraries 00003 00004 Copyright (c) 2007, 2008, 2010 Andreas Hartmetz <ahartmetz@gmail.com> 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., 51 Franklin Street, Fifth Floor, 00019 Boston, MA 02110-1301, USA. 00020 00021 */ 00022 00023 #include "kssld.h" 00024 00025 #include "ksslcertificatemanager.h" 00026 #include "kssld_adaptor.h" 00027 00028 #include <kconfig.h> 00029 #include <kconfiggroup.h> 00030 #include <QtCore/QFile> 00031 #include <kglobal.h> 00032 #include <kstandarddirs.h> 00033 #include <kdebug.h> 00034 #include <QtCore/QDate> 00035 #include <kpluginfactory.h> 00036 #include <kpluginloader.h> 00037 00038 00039 00040 K_PLUGIN_FACTORY(KSSLDFactory, registerPlugin<KSSLD>();) 00041 K_EXPORT_PLUGIN(KSSLDFactory("kssld")) 00042 //KDE_EXPORT void *__kde_do_unload; // TODO re-add support for this? 00043 00044 00045 class KSSLDPrivate 00046 { 00047 public: 00048 KSSLDPrivate() 00049 : config(QString::fromLatin1("ksslcertificatemanager"), KConfig::SimpleConfig) 00050 { 00051 struct strErr { 00052 const char *str; 00053 KSslError::Error err; 00054 }; 00055 00056 //hmmm, looks like these are all of the errors where it is possible to continue. 00057 const static strErr strError[] = { 00058 {"NoError", KSslError::NoError}, 00059 {"UnknownError", KSslError::UnknownError}, 00060 {"InvalidCertificateAuthority", KSslError::InvalidCertificateAuthorityCertificate}, 00061 {"InvalidCertificate", KSslError::InvalidCertificate}, 00062 {"CertificateSignatureFailed", KSslError::CertificateSignatureFailed}, 00063 {"SelfSignedCertificate", KSslError::SelfSignedCertificate}, 00064 {"RevokedCertificate", KSslError::RevokedCertificate}, 00065 {"InvalidCertificatePurpose", KSslError::InvalidCertificatePurpose}, 00066 {"RejectedCertificate", KSslError::RejectedCertificate}, 00067 {"UntrustedCertificate", KSslError::UntrustedCertificate}, 00068 {"ExpiredCertificate", KSslError::ExpiredCertificate}, 00069 {"HostNameMismatch", KSslError::HostNameMismatch} 00070 }; 00071 00072 for (int i = 0; i < int(sizeof(strError)/sizeof(strErr)); i++) { 00073 QString s = QString::fromLatin1(strError[i].str); 00074 KSslError::Error e = strError[i].err; 00075 stringToSslError.insert(s, e); 00076 sslErrorToString.insert(e, s); 00077 } 00078 } 00079 00080 KConfig config; 00081 QHash<QString, KSslError::Error> stringToSslError; 00082 QHash<KSslError::Error, QString> sslErrorToString; 00083 }; 00084 00085 00086 00087 KSSLD::KSSLD(QObject* parent, const QVariantList&) 00088 : KDEDModule(parent), 00089 d(new KSSLDPrivate()) 00090 { 00091 new KSSLDAdaptor(this); 00092 pruneExpiredRules(); 00093 } 00094 00095 00096 KSSLD::~KSSLD() 00097 { 00098 delete d; 00099 } 00100 00101 00102 void KSSLD::setRule(const KSslCertificateRule &rule) 00103 { 00104 if (rule.hostName().isEmpty()) { 00105 return; 00106 } 00107 KConfigGroup group = d->config.group(rule.certificate().digest().toHex()); 00108 00109 QStringList sl; 00110 00111 QString dtString = QString::fromLatin1("ExpireUTC "); 00112 dtString.append(rule.expiryDateTime().toString(Qt::ISODate)); 00113 sl.append(dtString); 00114 00115 if (rule.isRejected()) { 00116 sl.append(QString::fromLatin1("Reject")); 00117 } else { 00118 foreach (KSslError::Error e, rule.ignoredErrors()) 00119 sl.append(d->sslErrorToString.value(e)); 00120 } 00121 00122 if (!group.hasKey("CertificatePEM")) 00123 group.writeEntry("CertificatePEM", rule.certificate().toPem()); 00124 #ifdef PARANOIA 00125 else 00126 if (group.readEntry("CertificatePEM") != rule.certificate().toPem()) 00127 return; 00128 #endif 00129 group.writeEntry(rule.hostName(), sl); 00130 group.sync(); 00131 } 00132 00133 00134 void KSSLD::clearRule(const KSslCertificateRule &rule) 00135 { 00136 clearRule(rule.certificate(), rule.hostName()); 00137 } 00138 00139 00140 void KSSLD::clearRule(const QSslCertificate &cert, const QString &hostName) 00141 { 00142 KConfigGroup group = d->config.group(cert.digest().toHex()); 00143 group.deleteEntry(hostName); 00144 if (group.keyList().size() < 2) { 00145 group.deleteGroup(); 00146 } 00147 group.sync(); 00148 } 00149 00150 00151 void KSSLD::pruneExpiredRules() 00152 { 00153 // expired rules are deleted when trying to load them, so we just try to load all rules. 00154 // be careful about iterating over KConfig(Group) while changing it 00155 foreach (const QString &groupName, d->config.groupList()) { 00156 QByteArray certDigest = groupName.toLatin1(); 00157 foreach (const QString &key, d->config.group(groupName).keyList()) { 00158 if (key == QLatin1String("CertificatePEM")) { 00159 continue; 00160 } 00161 KSslCertificateRule r = rule(certDigest, key); 00162 } 00163 } 00164 } 00165 00166 00167 // check a domain name with subdomains for well-formedness and count the dot-separated parts 00168 static QString normalizeSubdomains(const QString &hostName, int *namePartsCount) 00169 { 00170 QString ret; 00171 int partsCount = 0; 00172 bool wasPrevDot = true; // -> allow no dot at the beginning and count first name part 00173 const int length = hostName.length(); 00174 for (int i = 0; i < length; i++) { 00175 const QChar c = hostName.at(i); 00176 if (c == QLatin1Char('.')) { 00177 if (wasPrevDot || (i + 1 == hostName.length())) { 00178 // consecutive dots or a dot at the end are forbidden 00179 partsCount = 0; 00180 ret.clear(); 00181 break; 00182 } 00183 wasPrevDot = true; 00184 } else { 00185 if (wasPrevDot) { 00186 partsCount++; 00187 } 00188 wasPrevDot = false; 00189 } 00190 ret.append(c); 00191 } 00192 00193 *namePartsCount = partsCount; 00194 return ret; 00195 } 00196 00197 00198 KSslCertificateRule KSSLD::rule(const QSslCertificate &cert, const QString &hostName) const 00199 { 00200 const QByteArray certDigest = cert.digest().toHex(); 00201 KConfigGroup group = d->config.group(certDigest); 00202 00203 KSslCertificateRule ret(cert, hostName); 00204 bool foundHostName = false; 00205 00206 int needlePartsCount; 00207 QString needle = normalizeSubdomains(hostName, &needlePartsCount); 00208 00209 // Find a rule for the hostname, either... 00210 if (group.hasKey(needle)) { 00211 // directly (host, site.tld, a.site.tld etc) 00212 if (needlePartsCount >= 1) { 00213 foundHostName = true; 00214 } 00215 } else { 00216 // or with wildcards 00217 // "tld" <- "*." and "site.tld" <- "*.tld" are not valid matches, 00218 // "a.site.tld" <- "*.site.tld" is 00219 while (--needlePartsCount >= 2) { 00220 const int dotIndex = needle.indexOf(QLatin1Char('.')); 00221 Q_ASSERT(dotIndex > 0); // if this fails normalizeSubdomains() failed 00222 needle.remove(0, dotIndex - 1); 00223 needle[0] = QChar::fromLatin1('*'); 00224 if (group.hasKey(needle)) { 00225 foundHostName = true; 00226 break; 00227 } 00228 needle.remove(0, 2); // remove "*." 00229 } 00230 } 00231 00232 if (!foundHostName) { 00233 //Don't make a rule with the failed wildcard pattern - use the original hostname. 00234 return KSslCertificateRule(cert, hostName); 00235 } 00236 00237 //parse entry of the format "ExpireUTC <date>, Reject" or 00238 //"ExpireUTC <date>, HostNameMismatch, ExpiredCertificate, ..." 00239 QStringList sl = group.readEntry(needle, QStringList()); 00240 00241 QDateTime expiryDt; 00242 // the rule is well-formed if it contains at least the expire date and one directive 00243 if (sl.size() >= 2) { 00244 QString dtString = sl.takeFirst(); 00245 if (dtString.startsWith(QLatin1String("ExpireUTC "))) { 00246 dtString.remove(0, 10/* length of "ExpireUTC " */); 00247 expiryDt = QDateTime::fromString(dtString, Qt::ISODate); 00248 } 00249 } 00250 00251 if (!expiryDt.isValid() || expiryDt < QDateTime::currentDateTime()) { 00252 //the entry is malformed or expired so we remove it 00253 group.deleteEntry(needle); 00254 //the group is useless once only the CertificatePEM entry left 00255 if (group.keyList().size() < 2) { 00256 group.deleteGroup(); 00257 } 00258 return ret; 00259 } 00260 00261 QList<KSslError::Error> ignoredErrors; 00262 bool isRejected = false; 00263 foreach (const QString &s, sl) { 00264 if (s == QLatin1String("Reject")) { 00265 isRejected = true; 00266 ignoredErrors.clear(); 00267 break; 00268 } 00269 if (!d->stringToSslError.contains(s)) { 00270 continue; 00271 } 00272 ignoredErrors.append(d->stringToSslError.value(s)); 00273 } 00274 00275 //Everything is checked and we can make ret valid 00276 ret.setExpiryDateTime(expiryDt); 00277 ret.setRejected(isRejected); 00278 ret.setIgnoredErrors(ignoredErrors); 00279 return ret; 00280 } 00281 00282 00283 #include "kssld.moc" 00284 #include "kssld_adaptor.moc"
This file is part of the KDE documentation.
Documentation copyright © 1996-2012 The KDE developers.
Generated on Thu May 10 2012 20:49:32 by doxygen 1.8.0 written by Dimitri van Heesch, © 1997-2006
Documentation copyright © 1996-2012 The KDE developers.
Generated on Thu May 10 2012 20:49:32 by doxygen 1.8.0 written by Dimitri van Heesch, © 1997-2006
KDE's Doxygen guidelines are available online.