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 #include <kstandarddirs.h> 00040 00041 using namespace KNS; 00042 00043 static QString gpgExecutable() 00044 { 00045 QString gpgExe = KStandardDirs::findExe( "gpg" ); 00046 if ( gpgExe.isEmpty() ) 00047 gpgExe = KStandardDirs::findExe( "gpg2" ); 00048 if ( gpgExe.isEmpty() ) 00049 return QLatin1String( "gpg" ); 00050 return gpgExe; 00051 } 00052 00053 Security::Security() 00054 { 00055 m_keysRead = false; 00056 m_gpgRunning = false; 00057 readKeys(); 00058 readSecretKeys(); 00059 } 00060 00061 00062 Security::~Security() 00063 { 00064 } 00065 00066 void Security::readKeys() 00067 { 00068 if (m_gpgRunning) { 00069 QTimer::singleShot(5, this, SLOT(readKeys())); 00070 return; 00071 } 00072 m_runMode = List; 00073 m_keys.clear(); 00074 m_process = new KProcess(); 00075 *m_process << gpgExecutable() 00076 << "--no-secmem-warning" 00077 << "--no-tty" 00078 << "--with-colon" 00079 << "--list-keys"; 00080 connect(m_process, SIGNAL(finished(int, QProcess::ExitStatus)), 00081 this, SLOT(slotFinished(int, QProcess::ExitStatus))); 00082 connect(m_process, SIGNAL(readyReadStandardOutput()), 00083 this, SLOT(slotReadyReadStandardOutput())); 00084 m_process->start(); 00085 if (!m_process->waitForStarted()) { 00086 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>")); 00087 delete m_process; 00088 m_process = 0; 00089 } else 00090 m_gpgRunning = true; 00091 } 00092 00093 void Security::readSecretKeys() 00094 { 00095 if (m_gpgRunning) { 00096 QTimer::singleShot(5, this, SLOT(readSecretKeys())); 00097 return; 00098 } 00099 m_runMode = ListSecret; 00100 m_process = new KProcess(); 00101 *m_process << gpgExecutable() 00102 << "--no-secmem-warning" 00103 << "--no-tty" 00104 << "--with-colon" 00105 << "--list-secret-keys"; 00106 connect(m_process, SIGNAL(finished(int, QProcess::ExitStatus)), 00107 this, SLOT(slotFinished(int, QProcess::ExitStatus))); 00108 connect(m_process, SIGNAL(readyReadStandardOutput()), 00109 this, SLOT(slotReadyReadStandardOutput())); 00110 m_process->start(); 00111 if (!m_process->waitForStarted()) { 00112 delete m_process; 00113 m_process = 0; 00114 } else 00115 m_gpgRunning = true; 00116 } 00117 00118 void Security::slotFinished(int exitCode, QProcess::ExitStatus exitStatus) 00119 { 00120 if (exitStatus != QProcess::NormalExit) { 00121 m_gpgRunning = false; 00122 delete m_process; 00123 m_process = 0; 00124 return; 00125 } 00126 switch (m_runMode) { 00127 case ListSecret: 00128 m_keysRead = true; 00129 break; 00130 case Verify: emit validityResult(m_result); 00131 break; 00132 case Sign: emit fileSigned(m_result); 00133 break; 00134 00135 } 00136 m_gpgRunning = false; 00137 delete m_process; 00138 m_process = 0; 00139 00140 Q_UNUSED(exitCode); 00141 } 00142 00143 void Security::slotReadyReadStandardOutput() 00144 { 00145 QString data; 00146 while (m_process->canReadLine()) { 00147 data = QString::fromLocal8Bit(m_process->readLine()); 00148 switch (m_runMode) { 00149 case List: 00150 case ListSecret: 00151 if (data.startsWith(QLatin1String("pub")) || data.startsWith(QLatin1String("sec"))) { 00152 KeyStruct key; 00153 if (data.startsWith(QLatin1String("pub"))) 00154 key.secret = false; 00155 else 00156 key.secret = true; 00157 QStringList line = data.split(':', QString::KeepEmptyParts); 00158 key.id = line[4]; 00159 QString shortId = key.id.right(8); 00160 QString trustStr = line[1]; 00161 key.trusted = false; 00162 if (trustStr == "u" || trustStr == "f") 00163 key.trusted = true; 00164 data = line[9]; 00165 key.mail = data.section('<', -1, -1); 00166 key.mail.truncate(key.mail.length() - 1); 00167 key.name = data.section('<', 0, 0); 00168 if (key.name.contains("(")) 00169 key.name = key.name.section('(', 0, 0); 00170 m_keys[shortId] = key; 00171 } 00172 break; 00173 case Verify: 00174 data = data.section(']', 1, -1).trimmed(); 00175 if (data.startsWith(QLatin1String("GOODSIG"))) { 00176 m_result &= SIGNED_BAD_CLEAR; 00177 m_result |= SIGNED_OK; 00178 QString id = data.section(' ', 1 , 1).right(8); 00179 if (!m_keys.contains(id)) { 00180 m_result |= UNKNOWN; 00181 } else { 00182 m_signatureKey = m_keys[id]; 00183 } 00184 } else 00185 if (data.startsWith(QLatin1String("NO_PUBKEY"))) { 00186 m_result &= SIGNED_BAD_CLEAR; 00187 m_result |= UNKNOWN; 00188 } else 00189 if (data.startsWith(QLatin1String("BADSIG"))) { 00190 m_result |= SIGNED_BAD; 00191 QString id = data.section(' ', 1 , 1).right(8); 00192 if (!m_keys.contains(id)) { 00193 m_result |= UNKNOWN; 00194 } else { 00195 m_signatureKey = m_keys[id]; 00196 } 00197 } else 00198 if (data.startsWith(QLatin1String("TRUST_ULTIMATE"))) { 00199 m_result &= SIGNED_BAD_CLEAR; 00200 m_result |= TRUSTED; 00201 } 00202 break; 00203 00204 case Sign: 00205 if (data.contains("passphrase.enter")) { 00206 KeyStruct key = m_keys[m_secretKey]; 00207 QPointer<KPasswordDialog> dlg = new KPasswordDialog(NULL); 00208 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)); 00209 if (dlg->exec()) { 00210 m_process->write(dlg->password().toLocal8Bit() + '\n'); 00211 } else { 00212 m_result |= BAD_PASSPHRASE; 00213 m_process->kill(); 00214 delete dlg; 00215 return; 00216 } 00217 delete dlg; 00218 } else 00219 if (data.contains("BAD_PASSPHRASE")) { 00220 m_result |= BAD_PASSPHRASE; 00221 } 00222 break; 00223 } 00224 } 00225 } 00226 00227 void Security::checkValidity(const QString& filename) 00228 { 00229 m_fileName = filename; 00230 slotCheckValidity(); 00231 } 00232 00233 void Security::slotCheckValidity() 00234 { 00235 if (!m_keysRead || m_gpgRunning) { 00236 QTimer::singleShot(5, this, SLOT(slotCheckValidity())); 00237 return; 00238 } 00239 if (m_keys.count() == 0) { 00240 emit validityResult(-1); 00241 return; 00242 } 00243 00244 m_result = 0; 00245 m_runMode = Verify; 00246 QFileInfo f(m_fileName); 00247 //check the MD5 sum 00248 QString md5sum; 00249 const char* c = ""; 00250 KMD5 context(c); 00251 QFile file(m_fileName); 00252 if (file.open(QIODevice::ReadOnly)) { 00253 context.reset(); 00254 context.update(file); 00255 md5sum = context.hexDigest(); 00256 file.close(); 00257 } 00258 file.setFileName(f.path() + "/md5sum"); 00259 if (file.open(QIODevice::ReadOnly)) { 00260 QByteArray md5sum_file; 00261 file.readLine(md5sum_file.data(), 50); 00262 if (!md5sum_file.isEmpty() && QString(md5sum_file).startsWith(md5sum)) 00263 m_result |= MD5_OK; 00264 file.close(); 00265 } 00266 m_result |= SIGNED_BAD; 00267 m_signatureKey.id = ""; 00268 m_signatureKey.name = ""; 00269 m_signatureKey.mail = ""; 00270 m_signatureKey.trusted = false; 00271 00272 //verify the signature 00273 m_process = new KProcess(); 00274 *m_process << gpgExecutable() 00275 << "--no-secmem-warning" 00276 << "--status-fd=2" 00277 << "--command-fd=0" 00278 << "--verify" 00279 << f.path() + "/signature" 00280 << m_fileName; 00281 connect(m_process, SIGNAL(finished(int, QProcess::ExitStatus)), 00282 this, SLOT(slotFinished(int, QProcess::ExitStatus))); 00283 connect(m_process, SIGNAL(readyReadStandardOutput()), 00284 this, SLOT(slotReadyReadStandardOutput())); 00285 m_process->start(); 00286 if (m_process->waitForStarted()) 00287 m_gpgRunning = true; 00288 else { 00289 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>")); 00290 emit validityResult(0); 00291 delete m_process; 00292 m_process = 0; 00293 } 00294 } 00295 00296 void Security::signFile(const QString &fileName) 00297 { 00298 m_fileName = fileName; 00299 slotSignFile(); 00300 } 00301 00302 void Security::slotSignFile() 00303 { 00304 if (!m_keysRead || m_gpgRunning) { 00305 QTimer::singleShot(5, this, SLOT(slotSignFile())); 00306 return; 00307 } 00308 00309 QStringList secretKeys; 00310 for (QMap<QString, KeyStruct>::Iterator it = m_keys.begin(); it != m_keys.end(); ++it) { 00311 if (it.value().secret) 00312 secretKeys.append(it.key()); 00313 } 00314 00315 if (secretKeys.count() == 0) { 00316 emit fileSigned(-1); 00317 return; 00318 } 00319 00320 m_result = 0; 00321 QFileInfo f(m_fileName); 00322 00323 //create the MD5 sum 00324 QString md5sum; 00325 const char* c = ""; 00326 KMD5 context(c); 00327 QFile file(m_fileName); 00328 if (file.open(QIODevice::ReadOnly)) { 00329 context.reset(); 00330 context.update(file); 00331 md5sum = context.hexDigest(); 00332 file.close(); 00333 } 00334 file.setFileName(f.path() + "/md5sum"); 00335 if (file.open(QIODevice::WriteOnly)) { 00336 QTextStream stream(&file); 00337 stream << md5sum; 00338 m_result |= MD5_OK; 00339 file.close(); 00340 } 00341 00342 if (secretKeys.count() > 1) { 00343 bool ok; 00344 secretKeys = KInputDialog::getItemList(i18n("Select Signing Key"), i18n("Key used for signing:"), secretKeys, QStringList(secretKeys[0]), false, &ok); 00345 if (ok) 00346 m_secretKey = secretKeys[0]; 00347 else { 00348 emit fileSigned(0); 00349 return; 00350 } 00351 } else 00352 m_secretKey = secretKeys[0]; 00353 00354 //verify the signature 00355 m_process = new KProcess(); 00356 *m_process << gpgExecutable() 00357 << "--no-secmem-warning" 00358 << "--status-fd=2" 00359 << "--command-fd=0" 00360 << "--no-tty" 00361 << "--detach-sign" 00362 << "-u" 00363 << m_secretKey 00364 << "-o" 00365 << f.path() + "/signature" 00366 << m_fileName; 00367 connect(m_process, SIGNAL(finished(int, QProcess::ExitStatus)), 00368 this, SLOT(slotFinished(int, QProcess::ExitStatus))); 00369 connect(m_process, SIGNAL(readyReadStandardOutput()), 00370 this, SLOT(slotReadyReadStandardOutput())); 00371 m_runMode = Sign; 00372 m_process->start(); 00373 if (m_process->waitForStarted()) 00374 m_gpgRunning = true; 00375 else { 00376 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>")); 00377 emit fileSigned(0); 00378 delete m_process; 00379 m_process = 0; 00380 } 00381 } 00382 00383 #include "security.moc"
KDE 4.7 API Reference