• Skip to content
  • Skip to link menu
KDE 4.7 API Reference
  • KDE API Reference
  • kdelibs
  • KDE Home
  • Contact Us
 

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 KNS3;
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&lt;%3&gt;</i><br />:</qt>", m_secretKey, key.name, key.mail));
00209                 if (dlg->exec()) {
00210                     m_process->write(dlg->password().toLocal8Bit() + '\n');
00211             delete dlg;
00212                 } else {
00213                     m_result |= BAD_PASSPHRASE;
00214                     m_process->kill();
00215             delete dlg;
00216                     return;
00217                 }
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"

KNewStuff

Skip menu "KNewStuff"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.7.5
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal