KNewStuff
security.cpp
Go to the documentation of this file.
00001 /* 00002 This file is part of KNewStuff2. 00003 Copyright (c) 2004, 2005 Andras Mantia <amantia@kde.org> 00004 Copyright (c) 2007 Josef Spillner <spillner@kde.org> 00005 00006 This library is free software; you can redistribute it and/or 00007 modify it under the terms of the GNU Lesser General Public 00008 License as published by the Free Software Foundation; either 00009 version 2.1 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 Lesser General Public License for more details. 00015 00016 You should have received a copy of the GNU Lesser General Public 00017 License along with this library. If not, see <http://www.gnu.org/licenses/>. 00018 */ 00019 00020 //app includes 00021 #include "security.h" 00022 00023 //qt includes 00024 #include <QtCore/QFile> 00025 #include <QtCore/QFileInfo> 00026 #include <QtCore/QPointer> 00027 #include <QtCore/QStringList> 00028 #include <QtCore/QTextIStream> 00029 #include <QtCore/QTimer> 00030 00031 //kde includes 00032 #include <kdebug.h> 00033 #include <kinputdialog.h> 00034 #include <klocale.h> 00035 #include <kcodecs.h> 00036 #include <kmessagebox.h> 00037 #include <kpassworddialog.h> 00038 #include <kprocess.h> 00039 00040 using namespace KNS; 00041 00042 Security::Security() 00043 { 00044 m_keysRead = false; 00045 m_gpgRunning = false; 00046 readKeys(); 00047 readSecretKeys(); 00048 } 00049 00050 00051 Security::~Security() 00052 { 00053 } 00054 00055 void Security::readKeys() 00056 { 00057 if (m_gpgRunning) { 00058 QTimer::singleShot(5, this, SLOT(readKeys())); 00059 return; 00060 } 00061 m_runMode = List; 00062 m_keys.clear(); 00063 m_process = new KProcess(); 00064 *m_process << "gpg2" 00065 << "--no-secmem-warning" 00066 << "--no-tty" 00067 << "--with-colon" 00068 << "--list-keys"; 00069 connect(m_process, SIGNAL(finished(int, QProcess::ExitStatus)), 00070 this, SLOT(slotFinished(int, QProcess::ExitStatus))); 00071 connect(m_process, SIGNAL(readyReadStandardOutput()), 00072 this, SLOT(slotReadyReadStandardOutput())); 00073 m_process->start(); 00074 if (!m_process->waitForStarted()) { 00075 KMessageBox::error(0L, i18n("<qt>Cannot start <i>gpg</i> and retrieve the available keys. Make sure that <i>gpg</i> is installed, otherwise verification of downloaded resources will not be possible.</qt>")); 00076 delete m_process; 00077 m_process = 0; 00078 } else 00079 m_gpgRunning = true; 00080 } 00081 00082 void Security::readSecretKeys() 00083 { 00084 if (m_gpgRunning) { 00085 QTimer::singleShot(5, this, SLOT(readSecretKeys())); 00086 return; 00087 } 00088 m_runMode = ListSecret; 00089 m_process = new KProcess(); 00090 *m_process << "gpg2" 00091 << "--no-secmem-warning" 00092 << "--no-tty" 00093 << "--with-colon" 00094 << "--list-secret-keys"; 00095 connect(m_process, SIGNAL(finished(int, QProcess::ExitStatus)), 00096 this, SLOT(slotFinished(int, QProcess::ExitStatus))); 00097 connect(m_process, SIGNAL(readyReadStandardOutput()), 00098 this, SLOT(slotReadyReadStandardOutput())); 00099 m_process->start(); 00100 if (!m_process->waitForStarted()) { 00101 delete m_process; 00102 m_process = 0; 00103 } else 00104 m_gpgRunning = true; 00105 } 00106 00107 void Security::slotFinished(int exitCode, QProcess::ExitStatus exitStatus) 00108 { 00109 if (exitStatus != QProcess::NormalExit) { 00110 m_gpgRunning = false; 00111 delete m_process; 00112 m_process = 0; 00113 return; 00114 } 00115 switch (m_runMode) { 00116 case ListSecret: 00117 m_keysRead = true; 00118 break; 00119 case Verify: emit validityResult(m_result); 00120 break; 00121 case Sign: emit fileSigned(m_result); 00122 break; 00123 00124 } 00125 m_gpgRunning = false; 00126 delete m_process; 00127 m_process = 0; 00128 00129 Q_UNUSED(exitCode); 00130 } 00131 00132 void Security::slotReadyReadStandardOutput() 00133 { 00134 QString data; 00135 while (m_process->canReadLine()) { 00136 data = QString::fromLocal8Bit(m_process->readLine()); 00137 switch (m_runMode) { 00138 case List: 00139 case ListSecret: 00140 if (data.startsWith(QLatin1String("pub")) || data.startsWith(QLatin1String("sec"))) { 00141 KeyStruct key; 00142 if (data.startsWith(QLatin1String("pub"))) 00143 key.secret = false; 00144 else 00145 key.secret = true; 00146 QStringList line = data.split(':', QString::KeepEmptyParts); 00147 key.id = line[4]; 00148 QString shortId = key.id.right(8); 00149 QString trustStr = line[1]; 00150 key.trusted = false; 00151 if (trustStr == "u" || trustStr == "f") 00152 key.trusted = true; 00153 data = line[9]; 00154 key.mail = data.section('<', -1, -1); 00155 key.mail.truncate(key.mail.length() - 1); 00156 key.name = data.section('<', 0, 0); 00157 if (key.name.contains("(")) 00158 key.name = key.name.section('(', 0, 0); 00159 m_keys[shortId] = key; 00160 } 00161 break; 00162 case Verify: 00163 data = data.section(']', 1, -1).trimmed(); 00164 if (data.startsWith(QLatin1String("GOODSIG"))) { 00165 m_result &= SIGNED_BAD_CLEAR; 00166 m_result |= SIGNED_OK; 00167 QString id = data.section(' ', 1 , 1).right(8); 00168 if (!m_keys.contains(id)) { 00169 m_result |= UNKNOWN; 00170 } else { 00171 m_signatureKey = m_keys[id]; 00172 } 00173 } else 00174 if (data.startsWith(QLatin1String("NO_PUBKEY"))) { 00175 m_result &= SIGNED_BAD_CLEAR; 00176 m_result |= UNKNOWN; 00177 } else 00178 if (data.startsWith(QLatin1String("BADSIG"))) { 00179 m_result |= SIGNED_BAD; 00180 QString id = data.section(' ', 1 , 1).right(8); 00181 if (!m_keys.contains(id)) { 00182 m_result |= UNKNOWN; 00183 } else { 00184 m_signatureKey = m_keys[id]; 00185 } 00186 } else 00187 if (data.startsWith(QLatin1String("TRUST_ULTIMATE"))) { 00188 m_result &= SIGNED_BAD_CLEAR; 00189 m_result |= TRUSTED; 00190 } 00191 break; 00192 00193 case Sign: 00194 if (data.contains("passphrase.enter")) { 00195 KeyStruct key = m_keys[m_secretKey]; 00196 QPointer<KPasswordDialog> dlg = new KPasswordDialog(NULL); 00197 dlg->setPrompt(i18n("<qt>Enter passphrase for key <b>0x%1</b>, belonging to<br /><i>%2<%3></i><br />:</qt>", m_secretKey, key.name, key.mail)); 00198 if (dlg->exec()) { 00199 m_process->write(dlg->password().toLocal8Bit() + '\n'); 00200 } else { 00201 m_result |= BAD_PASSPHRASE; 00202 m_process->kill(); 00203 delete dlg; 00204 return; 00205 } 00206 delete dlg; 00207 } else 00208 if (data.contains("BAD_PASSPHRASE")) { 00209 m_result |= BAD_PASSPHRASE; 00210 } 00211 break; 00212 } 00213 } 00214 } 00215 00216 void Security::checkValidity(const QString& filename) 00217 { 00218 m_fileName = filename; 00219 slotCheckValidity(); 00220 } 00221 00222 void Security::slotCheckValidity() 00223 { 00224 if (!m_keysRead || m_gpgRunning) { 00225 QTimer::singleShot(5, this, SLOT(slotCheckValidity())); 00226 return; 00227 } 00228 if (m_keys.count() == 0) { 00229 emit validityResult(-1); 00230 return; 00231 } 00232 00233 m_result = 0; 00234 m_runMode = Verify; 00235 QFileInfo f(m_fileName); 00236 //check the MD5 sum 00237 QString md5sum; 00238 const char* c = ""; 00239 KMD5 context(c); 00240 QFile file(m_fileName); 00241 if (file.open(QIODevice::ReadOnly)) { 00242 context.reset(); 00243 context.update(file); 00244 md5sum = context.hexDigest(); 00245 file.close(); 00246 } 00247 file.setFileName(f.path() + "/md5sum"); 00248 if (file.open(QIODevice::ReadOnly)) { 00249 QByteArray md5sum_file; 00250 file.readLine(md5sum_file.data(), 50); 00251 if (!md5sum_file.isEmpty() && QString(md5sum_file).startsWith(md5sum)) 00252 m_result |= MD5_OK; 00253 file.close(); 00254 } 00255 m_result |= SIGNED_BAD; 00256 m_signatureKey.id = ""; 00257 m_signatureKey.name = ""; 00258 m_signatureKey.mail = ""; 00259 m_signatureKey.trusted = false; 00260 00261 //verify the signature 00262 m_process = new KProcess(); 00263 *m_process << "gpg2" 00264 << "--no-secmem-warning" 00265 << "--status-fd=2" 00266 << "--command-fd=0" 00267 << "--verify" 00268 << f.path() + "/signature" 00269 << m_fileName; 00270 connect(m_process, SIGNAL(finished(int, QProcess::ExitStatus)), 00271 this, SLOT(slotFinished(int, QProcess::ExitStatus))); 00272 connect(m_process, SIGNAL(readyReadStandardOutput()), 00273 this, SLOT(slotReadyReadStandardOutput())); 00274 m_process->start(); 00275 if (m_process->waitForStarted()) 00276 m_gpgRunning = true; 00277 else { 00278 KMessageBox::error(0L, i18n("<qt>Cannot start <i>gpg</i> and check the validity of the file. Make sure that <i>gpg</i> is installed, otherwise verification of downloaded resources will not be possible.</qt>")); 00279 emit validityResult(0); 00280 delete m_process; 00281 m_process = 0; 00282 } 00283 } 00284 00285 void Security::signFile(const QString &fileName) 00286 { 00287 m_fileName = fileName; 00288 slotSignFile(); 00289 } 00290 00291 void Security::slotSignFile() 00292 { 00293 if (!m_keysRead || m_gpgRunning) { 00294 QTimer::singleShot(5, this, SLOT(slotSignFile())); 00295 return; 00296 } 00297 00298 QStringList secretKeys; 00299 for (QMap<QString, KeyStruct>::Iterator it = m_keys.begin(); it != m_keys.end(); ++it) { 00300 if (it.value().secret) 00301 secretKeys.append(it.key()); 00302 } 00303 00304 if (secretKeys.count() == 0) { 00305 emit fileSigned(-1); 00306 return; 00307 } 00308 00309 m_result = 0; 00310 QFileInfo f(m_fileName); 00311 00312 //create the MD5 sum 00313 QString md5sum; 00314 const char* c = ""; 00315 KMD5 context(c); 00316 QFile file(m_fileName); 00317 if (file.open(QIODevice::ReadOnly)) { 00318 context.reset(); 00319 context.update(file); 00320 md5sum = context.hexDigest(); 00321 file.close(); 00322 } 00323 file.setFileName(f.path() + "/md5sum"); 00324 if (file.open(QIODevice::WriteOnly)) { 00325 QTextStream stream(&file); 00326 stream << md5sum; 00327 m_result |= MD5_OK; 00328 file.close(); 00329 } 00330 00331 if (secretKeys.count() > 1) { 00332 bool ok; 00333 secretKeys = KInputDialog::getItemList(i18n("Select Signing Key"), i18n("Key used for signing:"), secretKeys, QStringList(secretKeys[0]), false, &ok); 00334 if (ok) 00335 m_secretKey = secretKeys[0]; 00336 else { 00337 emit fileSigned(0); 00338 return; 00339 } 00340 } else 00341 m_secretKey = secretKeys[0]; 00342 00343 //verify the signature 00344 m_process = new KProcess(); 00345 *m_process << "gpg2" 00346 << "--no-secmem-warning" 00347 << "--status-fd=2" 00348 << "--command-fd=0" 00349 << "--no-tty" 00350 << "--detach-sign" 00351 << "-u" 00352 << m_secretKey 00353 << "-o" 00354 << f.path() + "/signature" 00355 << m_fileName; 00356 connect(m_process, SIGNAL(finished(int, QProcess::ExitStatus)), 00357 this, SLOT(slotFinished(int, QProcess::ExitStatus))); 00358 connect(m_process, SIGNAL(readyReadStandardOutput()), 00359 this, SLOT(slotReadyReadStandardOutput())); 00360 m_runMode = Sign; 00361 m_process->start(); 00362 if (m_process->waitForStarted()) 00363 m_gpgRunning = true; 00364 else { 00365 KMessageBox::error(0L, i18n("<qt>Cannot start <i>gpg</i> and sign the file. Make sure that <i>gpg</i> is installed, otherwise signing of the resources will not be possible.</qt>")); 00366 emit fileSigned(0); 00367 delete m_process; 00368 m_process = 0; 00369 } 00370 } 00371 00372 #include "security.moc"
KDE 4.6 API Reference