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 for (int i = 0; i < hostName.length(); i++) { 00174 QChar c = hostName.at(i); 00175 if (c == QLatin1Char('.')) { 00176 if (wasPrevDot || (i + 1 == hostName.length())) { 00177 // consecutive dots or a dot at the end are forbidden 00178 partsCount = 0; 00179 ret.clear(); 00180 break; 00181 } 00182 wasPrevDot = true; 00183 } else { 00184 if (wasPrevDot) { 00185 partsCount++; 00186 } 00187 wasPrevDot = false; 00188 } 00189 ret.append(c); 00190 } 00191 00192 *namePartsCount = partsCount; 00193 return ret; 00194 } 00195 00196 00197 KSslCertificateRule KSSLD::rule(const QSslCertificate &cert, const QString &hostName) const 00198 { 00199 const QByteArray certDigest = cert.digest().toHex(); 00200 KConfigGroup group = d->config.group(certDigest); 00201 00202 KSslCertificateRule ret(cert, hostName); 00203 bool foundHostName = false; 00204 00205 int needlePartsCount; 00206 QString needle = normalizeSubdomains(hostName, &needlePartsCount); 00207 00208 // Find a rule for the hostname, either... 00209 if (group.hasKey(needle)) { 00210 // directly (site.tld, a.site.tld etc) 00211 if (needlePartsCount >= 2) { 00212 foundHostName = true; 00213 } 00214 } else { 00215 // or with wildcards 00216 // "tld" <- "*." and "site.tld" <- "*.tld" are not valid matches, 00217 // "a.site.tld" <- "*.site.tld" is 00218 while (--needlePartsCount >= 2) { 00219 const int dotIndex = needle.indexOf(QLatin1Char('.')); 00220 Q_ASSERT(dotIndex > 0); // if this fails normalizeSubdomains() failed 00221 needle.remove(0, dotIndex - 1); 00222 needle[0] = QChar::fromLatin1('*'); 00223 if (group.hasKey(needle)) { 00224 foundHostName = true; 00225 break; 00226 } 00227 needle.remove(0, 2); // remove "*." 00228 } 00229 } 00230 00231 if (!foundHostName) { 00232 //Don't make a rule with the failed wildcard pattern - use the original hostname. 00233 return KSslCertificateRule(cert, hostName); 00234 } 00235 00236 //parse entry of the format "ExpireUTC <date>, Reject" or 00237 //"ExpireUTC <date>, HostNameMismatch, ExpiredCertificate, ..." 00238 QStringList sl = group.readEntry(needle, QStringList()); 00239 00240 QDateTime expiryDt; 00241 // the rule is well-formed if it contains at least the expire date and one directive 00242 if (sl.size() >= 2) { 00243 QString dtString = sl.takeFirst(); 00244 if (dtString.startsWith(QLatin1String("ExpireUTC "))) { 00245 dtString.remove(0, 10/* length of "ExpireUTC " */); 00246 expiryDt = QDateTime::fromString(dtString, Qt::ISODate); 00247 } 00248 } 00249 00250 if (!expiryDt.isValid() || expiryDt < QDateTime::currentDateTime()) { 00251 //the entry is malformed or expired so we remove it 00252 group.deleteEntry(needle); 00253 //the group is useless once only the CertificatePEM entry left 00254 if (group.keyList().size() < 2) { 00255 group.deleteGroup(); 00256 } 00257 return ret; 00258 } 00259 00260 QList<KSslError::Error> ignoredErrors; 00261 bool isRejected = false; 00262 foreach (const QString &s, sl) { 00263 if (s == QLatin1String("Reject")) { 00264 isRejected = true; 00265 ignoredErrors.clear(); 00266 break; 00267 } 00268 if (!d->stringToSslError.contains(s)) { 00269 continue; 00270 } 00271 ignoredErrors.append(d->stringToSslError.value(s)); 00272 } 00273 00274 //Everything is checked and we can make ret valid 00275 ret.setExpiryDateTime(expiryDt); 00276 ret.setRejected(isRejected); 00277 ret.setIgnoredErrors(ignoredErrors); 00278 return ret; 00279 } 00280 00281 00282 #include "kssld.moc" 00283 #include "kssld_adaptor.moc"
KDE 4.6 API Reference